/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-core.js  V1.31
 * @author K'naan
 * -- Modified from dwz.core.js (author:ZhangHuihua@msn.com)
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-core.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    var BJUI = {
        JSPATH: 'BJUI/',
        PLUGINPATH: 'BJUI/plugins/',
        IS_DEBUG: false,
        KeyPressed: { //key press state
            ctrl: false,
            shift: false
        },
        keyCode: {
            ENTER: 13, ESC: 27, END: 35, HOME: 36,
            SHIFT: 16, CTRL: 17, TAB: 9,
            LEFT: 37, RIGHT: 39, UP: 38, DOWN: 40,
            DELETE: 46, BACKSPACE: 8
        },
        eventType: {
            initUI: 'bjui.initUI',         // When document load completed or ajax load completed, B-JUI && Plugins init 
            beforeInitUI: 'bjui.beforeInitUI',   // If your DOM do not init [add to DOM attribute 'data-noinit="true"']
            afterInitUI: 'bjui.afterInitUI',    // 
            ajaxStatus: 'bjui.ajaxStatus',     // When performing ajax request, display or hidden progress bar
            resizeGrid: 'bjui.resizeGrid',     // When the window or dialog resize completed
            beforeAjaxLoad: 'bjui.beforeAjaxLoad', // When perform '$.fn.ajaxUrl', to do something...

            beforeLoadNavtab: 'bjui.beforeLoadNavtab',
            beforeLoadDialog: 'bjui.beforeLoadDialog',
            afterLoadNavtab: 'bjui.afterLoadNavtab',
            afterLoadDialog: 'bjui.afterLoadDialog',
            beforeCloseNavtab: 'bjui.beforeCloseNavtab',
            beforeCloseDialog: 'bjui.beforeCloseDialog',
            afterCloseNavtab: 'bjui.afterCloseNavtab',
            afterCloseDialog: 'bjui.afterCloseDialog'
        },
        formColWidth: { L: 900, M: 680, S: 360, SS: 240 },
        pageInfo: { pageCurrent: 'pageCurrent', pageSize: 'pageSize', orderField: 'orderField', orderDirection: 'orderDirection' },
        alertMsg: { displayPosition: 'topcenter', alertTimeout: 6000 }, //alertmsg display position && close timeout
        ajaxTimeout: 30000,
        statusCode: { ok: 200, error: 300, timeout: 301 },
        keys: { statusCode: 'statusCode', message: 'message' },
        ui: {
            windowWidth: 0,
            showSideWidth: 990,
            sidenavWidth: 260,
            offsetWidth: 15,
            displayFirst: true,
            showSlidebar: true,      // After the B-JUI initialization, display slidebar
            clientPaging: true,      // Response paging and sorting information on the client
            overwriteHomeTab: false      // When open an undefined id of navtab, whether overwrite the home navtab
        },
        debug: function (msg) {
            if (this.IS_DEBUG) {
                if (typeof (console) != 'undefined') console.log(msg)
                else alert(msg)
            }
        },
        loginInfo: {
            url: 'login.html',
            title: 'Login',
            width: 420,
            height: 260,
            mask: true,
            resizable: false,
            maxable: false
        },
        loadLogin: function () {
            var login = this.loginInfo

            BJUI.dialog({ id: 'bjui-login', url: login.url, title: login.title, width: login.width, height: login.height, mask: login.mask, resizable: login.resizable, maxable: login.maxable })
        },
        init: function (options) {
            var op = $.extend({}, options)

            $.extend(BJUI.keys, op.keys)
            $.extend(BJUI.statusCode, op.statusCode)
            $.extend(BJUI.pageInfo, op.pageInfo)
            $.extend(BJUI.formColWidth, op.formColWidth)
            $.extend(BJUI.alertMsg, op.alertMsg)
            $.extend(BJUI.loginInfo, op.loginInfo)
            $.extend(BJUI.ui, op.ui)

            if (op.JSPATH) this.JSPATH = op.JSPATH
            if (op.PLUGINPATH) this.PLUGINPATH = op.PLUGINPATH
            if (op.ajaxTimeout) this.ajaxTimeout = op.ajaxTimeout

            this.IS_DEBUG = op.debug || false
            this.initEnv()

            if ((!$.cookie || !$.cookie('bjui_theme')) && op.theme) $(this).theme('setTheme', op.theme)
        },
        initEnv: function () {
            $(window).resize(function () {
                BJUI.initLayout()

                setTimeout(function () { $(this).trigger(BJUI.eventType.resizeGrid) }, 30)
            })

            setTimeout(function () {
                $(document).initui()

                var $collapse = $('#bjui-navbar-collapse'), left = $collapse.prev().width()

                $collapse.css('left', left).data('position.left', left)

                BJUI.initLayout()

                $('#bjui-top-collapse').on('click.theme', '.dropdown-toggle', function () {
                    var $dropdown = $(this).next(), $topcollapse = $('#bjui-top-collapse')

                    if (!$topcollapse.hasClass('navbar-show')) {
                        $topcollapse.attr('style', $dropdown.is(':visible') ? '' : 'overflow:visible !important;')
                    }
                })

                $('[data-toggle="collapsenavbar"]').click(function () {
                    var $this = $(this), $target = $($this.data('target')), $parent = $this.closest('.navbar-header')

                    if ($target.length) {
                        $target.toggleClass('navbar-show')
                        $parent.toggleClass('navbar-show')

                        $(document).on('click', function (e) {
                            if (!($target.has(e.target).length) && !($parent.has(e.target).length)) {
                                $target.removeClass('navbar-show')
                                $parent.removeClass('navbar-show')
                            }
                        })
                    }
                })
            }, 10)
        },
        initLayout: function () {
            var ww = $(window).width(), hh = $(window).height(),
                $top = $('#bjui-top'), $navbar = $('#bjui-navbar'), $collapse = $('#bjui-top-collapse, #bjui-navbar-collapse'),
                th = $top.height(), nh = parseInt($navbar.css('min-height'), 10),
                eventName = 'click.bjui.sidenav.hide', opts = {},
                $navtab = $('#bjui-navtab'), $sidenav = $('#bjui-sidenav'), $sidenavcol = $('#bjui-sidenav-col'),
                $sidenavarrow = $('#bjui-sidenav-arrow'), $sidenavbtn = $('#bjui-sidenav-btn'), $sidenavbox = $('#bjui-sidenav-box'),
                tnh = $sidenavbtn.outerHeight() - 2

            $('#bjui-navtab .tabsPageContent').height(hh - th - nh - tnh)
            $sidenav.height(hh - th - nh - 1)
            $sidenavcol.width(BJUI.ui.sidenavWidth)

            $collapse.scrollTop(5).eq(0).removeAttr('style')
            $collapse.each(function () {
                var $this = $(this), $collapsebtn = $this.prev().find('.navbar-toggle'), isCollapse = ($this.scrollTop() == 5 || $this.hasClass('navbar-show'))

                isCollapse && $this.scrollTop(0)

                $this.toggleClass('position', isCollapse)
                $collapsebtn.toggleClass('position', isCollapse)

                if ($this.data('position.left') && (ww / 2) < $this.data('position.left')) {
                    $this.prev().find('> .navbar-brand').hide()
                    $this.css('left', 0)
                } else {
                    $this.prev().find('> .navbar-brand').show()
                    $this.css('left', $this.data('position.left'))
                }
            })

            if (ww < BJUI.ui.showSideWidth) {
                $sidenavcol.css('left', -(BJUI.ui.sidenavWidth + BJUI.ui.offsetWidth)).addClass('autohide')
                $navtab.css('margin-left', 0)
                $sidenavbtn.show()
            } else {
                $sidenavcol.css('left', 0).removeClass('autohide')
                $navtab.css('margin-left', (BJUI.ui.sidenavWidth + BJUI.ui.offsetWidth))
                $sidenavbtn.hide()
            }

            $sidenavarrow.off(eventName).on(eventName, function () {
                $sidenavcol
                    .stop()
                    .animate({
                        left: -(BJUI.ui.sidenavWidth + BJUI.ui.offsetWidth)
                    }, 'fast', function () {
                        $sidenavbtn.show()

                        $(window).trigger(BJUI.eventType.resizeGrid)
                    })

                $navtab
                    .stop()
                    .animate({
                        'margin-left': 0
                    }, 'fast')
            })

            $sidenavbtn.off(eventName).on(eventName, function () {
                $sidenavbtn.hide()

                $sidenavcol
                    .stop()
                    .animate({
                        left: 0
                    }, 'fast', function () {
                        $(window).trigger(BJUI.eventType.resizeGrid)
                    })

                opts['margin-left'] = (BJUI.ui.sidenavWidth + BJUI.ui.offsetWidth)
                if (ww < BJUI.ui.showSideWidth)
                    opts['margin-left'] = 0

                $navtab
                    .stop()
                    .animate(opts, 'fast')
            })

            /* fixed pageFooter */
            /*setTimeout(function() {
                $('#bjui-navtab > .tabsPageContent > .navtabPage').resizePageH()
                $('#bjui-navtab > .tabsPageContent > .navtabPage').find('.bjui-layout').resizePageH()
            }, 10)*/
        },
        regional: {},
        setRegional: function (key, value) {
            BJUI.regional[key] = value
        },
        getRegional: function (key) {
            if (String(key).indexOf('.') >= 0) {
                var msg, arr = String(key).split('.')

                for (var i = 0; i < arr.length; i++) {
                    if (!msg) msg = BJUI.regional[arr[i]]
                    else msg = msg[arr[i]]
                }

                return msg
            } else {
                return BJUI.regional[key]
            }
        },
        doRegional: function (frag, regional) {
            $.each(regional, function (k, v) {
                frag = frag.replaceAll('#' + k + '#', v)
            })

            return frag
        },
        // is ie browser
        isIE: function (ver) {
            var b = document.createElement('b')

            b.innerHTML = '<!--[if IE ' + ver + ']><i></i><![endif]-->'

            return b.getElementsByTagName('i').length === 1
        },
        StrBuilder: function () {
            return new StrBuilder()
        }
    }

    function StrBuilder() {
        this.datas = new Array()
    }

    StrBuilder.prototype.add = function (str) {
        if (typeof str !== 'undefined') this.datas.push(str)
        return this
    }

    StrBuilder.prototype.toString = function (str) {
        var string = this.datas.join(str || '')

        this.clear()

        return string
    }

    StrBuilder.prototype.isEmpty = function () {
        return this.datas.length == 0
    }

    StrBuilder.prototype.clear = function () {
        this.datas = []
        this.datas.length = 0
    }

    window.BJUI = BJUI

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-regional.zh-CN.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-regional.zh-CN.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    $(function () {

        /* 消息提示框 */
        BJUI.setRegional('alertmsg', {
            title: { error: transLang('错误提示'), info: transLang('信息提示'), warn: transLang('警告信息'), correct: transLang('成功信息'), confirm: transLang('确认信息'), prompt: transLang('确认信息') },
            btnMsg: { ok: transLang('确定'), yes: transLang('是'), no: transLang('否'), cancel: transLang('取消') }
        })

        /* dialog */
        BJUI.setRegional('dialog', {
            close: transLang('关闭'),
            maximize: transLang('最大化'),
            restore: transLang('还原'),
            minimize: transLang('最小化'),
            title: transLang('弹出窗口')
        })

        /* order by */
        BJUI.setRegional('orderby', {
            asc: transLang('升序'),
            desc: transLang('降序')
        })

        /* 分页 */
        BJUI.setRegional('pagination', {
            total: transLang('当前页/总页数'),
            first: transLang('首页'),
            last: transLang('末页'),
            prev: transLang('上一页'),
            next: transLang('下一页'),
            jumpto: transLang('输入跳转页码，回车确认'),
            jump: transLang('跳转'),
            page: transLang('页'),
            refresh: transLang('刷新'),
            unit: transLang('条'),
            eachpage: transLang('每页'),
            ttl: transLang('共')
        })

        BJUI.setRegional('datagrid', {
            asc: transLang('升序'),
            desc: transLang('降序'),
            showhide: transLang('显示/隐藏 列'),
            filter: transLang('过滤'),
            clear: transLang('清除'),
            lock: transLang('锁定列'),
            unlock: transLang('解除锁定'),
            add: transLang('添加'),
            edit: transLang('编辑'),
            save: transLang('保存'),
            update: transLang('更新'),
            cancel: transLang('取消'),
            del: transLang('删除'),
            prev: transLang('上一条'),
            next: transLang('下一条'),
            refresh: transLang('刷新'),
            query: transLang('查询'),
            'import': transLang('导入'),
            'export': transLang('导出'),
            exportf: transLang('导出筛选'),
            all: transLang('全部'),
            'true': transLang('是'),
            'false': transLang('否'),
            noData: transLang('没有数据！'),
            fAndS: transLang('过滤和排序！'),
            expandMsg: transLang('点我展开行！'),
            shrinkMsg: transLang('点我收缩行！'),
            selectMsg: transLang('未选中任何行！'),
            editMsg: transLang('请先保存编辑行！'),
            saveMsg: transLang('没有需要保存的行！'),
            delMsg: transLang('确定要删除该行吗？'),
            delMsgM: transLang('确定要删除选中行？'),
            errorData: transLang('未获取到正确的数据！'),
            failData: transLang('请求datagrid数据失败！')
        })

        BJUI.setRegional('findgrid', {
            choose: transLang('选择选中项'),
            append: transLang('追加选择'),
            empty: transLang('清空现有值')
        })

        /* ajax加载提示 */
        BJUI.setRegional('progressmsg', transLang('正在努力加载数据，请稍等...'))

        /* 日期选择器 */
        BJUI.setRegional('datepicker', {
            close: transLang('关闭'),
            prev: transLang('上月'),
            next: transLang('下月'),
            clear: transLang('清空'),
            ok: transLang('确定'),
            dayNames:
                [
                    transLang('日'),
                    transLang('一'),
                    transLang('二'),
                    transLang('三'),
                    transLang('四'),
                    transLang('五'),
                    transLang('六')
                ],
            monthNames:
                [
                    transLang('一月'),
                    transLang('二月'),
                    transLang('三月'),
                    transLang('四月'),
                    transLang('五月'),
                    transLang('六月'),
                    transLang('七月'),
                    transLang('八月'),
                    transLang('九月'),
                    transLang('十月'),
                    transLang('十一月'),
                    transLang('十二月')
                ]
        })

        /* navtab右键菜单  */
        BJUI.setRegional('navtabCM', {
            refresh: transLang('刷新本标签'),
            close: transLang('关闭本标签'),
            closeother: transLang('关闭其他标签'),
            closeall: transLang('关闭所有标签')
        })

        /* dialog右键菜单 */
        BJUI.setRegional('dialogCM', {
            refresh: transLang('刷新本窗口'),
            close: transLang('关闭本窗口'),
            closeother: transLang('关闭其他窗口'),
            closeall: transLang('关闭所有窗口')
        })

        /* 503错误提示 */
        BJUI.setRegional('statusCode_503', transLang('服务器当前负载过大或者正在维护！'))

        /* AJAX 状态返回 0 提示 */
        BJUI.setRegional('ajaxnosend', transLang('与服务器的通信中断，请检查URL链接或服务器状态！'))

        /* timeout提示 */
        BJUI.setRegional('sessiontimeout', transLang('会话超时，请重新登陆！'))

        /* 占位符对应选择器无有效值提示 */
        BJUI.setRegional('plhmsg', transLang('占位符对应的选择器无有效值！'))

        /* 未定义复选框组名提示 */
        BJUI.setRegional('nocheckgroup', transLang('未定义选中项的组名[复选框的"data-group"]！'))

        /* 未选中复选框提示 */
        BJUI.setRegional('notchecked', transLang('未选中任何一项！'))

        /* 未选中下拉菜单提示 */
        BJUI.setRegional('selectmsg', transLang('请选择一个选项！'))

        /* 表单验证错误提示信息 */
        BJUI.setRegional('validatemsg', transLang('提交的表单中 [{0}] 个字段有错误，请更正后再提交！'))

        /* ID检查 */
        BJUI.setRegional('idChecked', transLang('不规范，ID需以字母开头，组成部分包括（0-9，字母，中横线，下划线）'))

        /* 框架名称 */
        BJUI.setRegional('uititle', 'B-JUI')

        /* 主navtab标题 */
        BJUI.setRegional('maintab', transLang('我的主页'))

        /**
         * 
         *  Plugins regional setting
         * 
         */
        /* nice validate - Global configuration */
        $.validator && $.validator.config({
            // Custom rules
            rules: {
                digits: [/^\d+$/, transLang('请输入整数')]
                , number: [/^[\-\+]?((([0-9]{1,3})([,][0-9]{3})*)|([0-9]+))?([\.]([0-9]+))?$/, transLang('请输入有效的数字')]
                , letters: [/^[a-z]+$/i, transLang('{0}只能输入字母')]
                , upletterandnumber: [/^[A-Z0-9]+$/, transLang('只能输入大写字母和数字')]
                , tel: [/^(?:(?:0\d{2,3}[\- ]?[1-9]\d{6,7})|(?:[48]00[\- ]?[1-9]\d{6}))$/, transLang("请填写有效的电话号码")]
                , mobile: [/^1[3-9]\d{9}$/, transLang('手机号格式不正确')]
                , email: [/^[\w\+\-]+(\.[\w\+\-]+)*@[a-z\d\-]+(\.[a-z\d\-]+)*\.([a-z]{2,4})$/i, transLang("请填写有效的邮箱")]
                , qq: [/^[1-9]\d{4,}$/, transLang('QQ号格式不正确')]
                //,date: [/^\d{4}-\d{1,2}-\d{1,2}$/, '请输入正确的日期,例:yyyy-mm-dd']
                , date: [/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/, transLang('请输入正确的日期，例：yyyy-MM-dd')]
                //,time: [/^([01]\d|2[0-3])(:[0-5]\d){1,2}$/, '请输入正确的时间,例:14:30或14:30:00']
                , time: [/^(2[0123]|(1|0?)[0-9]){1}:([0-5][0-9]){1}:([0-5][0-9]){1}$/, transLang('请输入正确的时间，例：HH:mm:ss')]
                , datetime: [/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])\s+(2[0123]|(1|0?)[0-9]){1}:([0-5][0-9]){1}:([0-5][0-9]){1}$/,
                    transLang('请输入正确的日期时间，例：yyyy-MM-dd HH:mm:ss')]
                , IDcard: [/^\d{6}(19|2\d)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)?$/, transLang("请填写正确的身份证号码")]
                , url: [/^(https?|s?ftp):\/\/\S+$/i, transLang("请填写有效的网址")]
                , zipcode: [/^\d{6}$/, transLang("请检查邮政编码格式")]
                , chinese: [/^[\u0391-\uFFE5]+$/, transLang('请输入中文')]
                , username: [/^\w{3,12}$/, transLang('请输入3-12位数字、字母、下划线')]
                , password: [/^[\S]{6,16}$/, transLang("请填写6-16位字符，不能包含空格")]
                , pattern: function (element, params) {
                    if (!params) return true

                    var date = element.value.parseDate(params)

                    return (!date ? this.renderMsg(transLang('错误的日期时间格式！'), params) : true)
                }
                , accept: function (element, params) {
                    if (!params) return true;
                    var ext = params[0],
                        value = $(element).val();
                    return (ext === '*') ||
                        (new RegExp(".(?:" + ext + ")$", "i")).test(value) ||
                        this.renderMsg(transLang("只接受{1}后缀的文件"), ext.replace(/\|/g, ','));
                }
            },

            // Default error messages
            messages: {
                0: transLang("此处"),
                fallback: transLang("{0}格式不正确"),
                loading: transLang("正在验证..."),
                error: transLang("网络异常"),
                timeout: transLang("请求超时"),
                required: transLang("{0}不能为空"),
                remote: transLang("{0}已被使用"),
                integer: {
                    '*': transLang("请填写整数"),
                    '+': transLang("请填写正整数"),
                    '+0': transLang("请填写正整数或0"),
                    '-': transLang("请填写负整数"),
                    '-0': transLang("请填写负整数或0")
                },
                match: {
                    eq: transLang("{0}与{1}不一致"),
                    neq: transLang("{0}与{1}不能相同"),
                    lt: transLang("{0}必须小于{1}"),
                    gt: transLang("{0}必须大于{1}"),
                    lte: transLang("{0}不能大于{1}"),
                    gte: transLang("{0}不能小于{1}")
                },
                range: {
                    rg: transLang("请填写{1}到{2}的数"),
                    gte: transLang("请填写不小于{1}的数"),
                    lte: transLang("请填写最大{1}的数"),
                    gtlt: transLang("请填写{1}到{2}之间的数"),
                    gt: transLang("请填写大于{1}的数"),
                    lt: transLang("请填写小于{1}的数")
                },
                checked: {
                    eq: transLang("请选择{1}项"),
                    rg: transLang("请选择{1}到{2}项"),
                    gte: transLang("请至少选择{1}项"),
                    lte: transLang("请最多选择{1}项")
                },
                length: {
                    eq: transLang("请填写{1}个字符"),
                    rg: transLang("请填写{1}到{2}个字符"),
                    gte: transLang("请至少填写{1}个字符"),
                    lte: transLang("请最多填写{1}个字符"),
                    eq_2: "",
                    rg_2: "",
                    gte_2: "",
                    lte_2: ""
                }
            }
        })

    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-frag.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-frag.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    BJUI.setRegional('alertmsg', {
        title: { error: 'Error', info: 'Info', warn: 'Warning', correct: 'Correct', confirm: 'Confirm', prompt: 'Prompt' },
        btnMsg: { ok: 'OK', yes: 'YES', no: 'NO', cancel: 'Cancel' }
    })

    BJUI.setRegional('dialog', {
        close: 'Close',
        maximize: 'Maximize',
        restore: 'Restore',
        minimize: 'Minimize',
        title: 'Popup window'
    })

    BJUI.setRegional('orderby', {
        asc: 'Asc',
        desc: 'Desc'
    })

    BJUI.setRegional('pagination', {
        first: 'First page',
        last: 'Last page',
        prev: 'Prev page',
        next: 'Next page',
        jumpto: 'Jump page number',
        jump: 'Jump'
    })

    BJUI.setRegional('datagrid', {
        asc: 'ASC',
        desc: 'DESC',
        showhide: 'Show/Hide columns',
        filter: 'Filter',
        clear: 'Clear',
        lock: 'Lock',
        unlock: 'Unlock',
        add: 'Add',
        edit: 'Edit',
        save: 'Save',
        update: 'Update',
        cancel: 'Cancel',
        del: 'Delete',
        prev: 'Prev',
        next: 'Next',
        refresh: 'Refresh',
        query: 'Query',
        'import': 'Import',
        'export': 'Export',
        exportf: 'Export filter',
        all: 'All',
        'true': 'True',
        'false': 'False',
        noData: 'No data!',
        fAndS: 'Filter && Sort!',
        expandMsg: 'Click here to expand the tr!',
        shrinkMsg: 'Click here to shrink the tr!',
        selectMsg: 'Not selected any rows!',
        saveMsg: 'No rows need to save!',
        editMsg: 'Please save the edited row!',
        delMsg: 'Sure you want to delete this row?',
        delMsgM: 'Sure you want to delete selected rows?',
        errorData: 'Did not get the correct data!',
        failData: 'Request data failed!'
    })

    BJUI.setRegional('findgrid', {
        choose: 'Choose the selected item',
        append: 'Append choose the selected item',
        empty: 'Empty existing values'
    })

    BJUI.setRegional('progressmsg', 'Data loading, please waiting...')

    BJUI.setRegional('datepicker', {
        close: 'Close',
        prev: 'Prev month',
        next: 'Next month',
        clear: 'Clear',
        ok: 'OK',
        dayNames: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
        monthNames: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
    })

    BJUI.setRegional('navtabCM', {
        refresh: 'Refresh navtab',
        close: 'Close navtab',
        closeother: 'Close other navtab',
        closeall: 'Close all navtab'
    })

    BJUI.setRegional('dialogCM', {
        refresh: 'Refresh dialog',
        close: 'Close dialog',
        closeother: 'Close other dialog',
        closeall: 'Close all dialog'
    })

    BJUI.setRegional('statusCode_503', 'HTTP status 503, the current server load is too large or is down for maintenance!')

    BJUI.setRegional('ajaxnosend', 'Communication with the server is interrupted, please check the URL link or server status!')

    BJUI.setRegional('sessiontimout', 'Session timeout, please login!')

    BJUI.setRegional('plhmsg', 'Placeholder corresponding selector None Valid!')

    BJUI.setRegional('nocheckgroup', 'Undefined group name selected item [check box "data-group"]!')

    BJUI.setRegional('notchecked', 'Unchecked any one!')

    BJUI.setRegional('selectmsg', 'Please select one option!')

    BJUI.setRegional('validatemsg', 'Submitted form data has [{0}] field an error, please after modified submitting!')

    BJUI.setRegional('idChecked', 'is not standardized, ID need to start with a letter, components include (0-9, letters, hyphens, underscore)')

    BJUI.setRegional('uititle', 'B-JUI')

    BJUI.setRegional('maintab', 'My home')


    window.FRAG = {
        dialog: '<div class="bjui-dialog bjui-dialog-container" style="top:150px; left:300px;">' +
            '    <div class="dialogHeader" onselectstart="return false;" oncopy="return false;" onpaste="return false;" oncut="return false;">' +
            '        <a class="close" href="javascript:;" title="#close#"><i class="fa fa-times"></i></a>' +
            '        <a class="maximize" href="javascript:;" title="#maximize#"><i class="fa fa-square-o"></i></a>' +
            '        <a class="restore" href="javascript:;" title="#restore#"><i class="fa fa-clone fa-rotate-90"></i></a>' +
            '        <a class="minimize" href="javascript:;" title="#minimize#"><i class="fa fa-minus"></i></a>' +
            '        <h1><span><i class="fa fa-th-large"></i></span> <span class="title">#title#</span></h1>' +
            '    </div>' +
            '    <div class="dialogContent unitBox"></div>' +
            '    <div class="resizable_h_l" tar="nw"></div>' +
            '    <div class="resizable_h_r" tar="ne"></div>' +
            '    <div class="resizable_h_c" tar="n"></div>' +
            '    <div class="resizable_c_l" tar="w" style="height:100%;"></div>' +
            '    <div class="resizable_c_r" tar="e" style="height:100%;"></div>' +
            '    <div class="resizable_f_l" tar="sw"></div>' +
            '    <div class="resizable_f_r" tar="se"></div>' +
            '    <div class="resizable_f_c" tar="s"></div>' +
            '</div>'
        ,
        taskbar: '<div id="bjui-taskbar" style="left:0px; display:none;">' +
            '    <div class="taskbarContent">' +
            '        <ul></ul>' +
            '    </div>' +
            '    <div class="taskbarLeft taskbarLeftDisabled"><i class="fa fa-angle-double-left"></i></div>' +
            '    <div class="taskbarRight"><i class="fa fa-angle-double-right"></i></div>' +
            '</div>'
        ,
        splitBar: '<div id="bjui-splitBar"></div>',
        splitBarProxy: '<div id="bjui-splitBarProxy"></div>',
        resizable: '<div id="bjui-resizable" class="bjui-resizable"></div>',
        alertBackground: '<div class="bjui-alertBackground"></div>',
        maskBackground: '<div class="bjui-maskBackground bjui-ajax-mask"></div>',
        maskProgress: '<div class="bjui-maskProgress bjui-ajax-mask"><i class="fa fa-cog fa-spin"></i>&nbsp;&nbsp;#progressmsg#<div class="progressBg"><div class="progress"></div></div></div>',
        progressBar_custom: '<div id="bjui-progressBar-custom" class="progressBar"><i class="fa fa-cog fa-spin"></i> <span></span></div>',
        dialogMask: '<div class="bjui-dialogBackground"></div>',
        orderby: '<a href="javascript:;" class="order asc" data-order-direction="asc" title="#asc#"><i class="fa fa-angle-up"></i></a>' +
            '<a href="javascript:;" class="order desc" data-order-direction="desc" title="#desc#"><i class="fa fa-angle-down"></i></a>'
        ,
        slidePanel: '<div class="panel panel-default">' +
            '    <div class="panel-heading">' +
            '        <h4 class="panel-title"><a data-toggle="collapse" data-parent="#bjui-accordionmenu" href="##id#" class="#class#">#icon#&nbsp;#title#<b>#righticon#</b></a></h4>' +
            '    </div>' +
            '    <div id="#id#" class="panel-collapse collapse#bodyclass#">' +
            '        <div class="panel-body">' +
            '        </div>' +
            '    </div>' +
            '</div>'
        ,
        gridPaging: '<ul class="pagination">' +
            '    <li class="page-total">' +
            '        <span title="#total#">#count#</span>' +
            '    </li>' +
            '    <li class="page-jumpto"><span class="page-input"><input class="form-control input-sm-pages" type="text" size="3.2" value="#pageCurrent#" title="#jumpto#"></span></li>' +
            '    <li class="page-first btn-nav">' +
            '        <a href="javascript:;" title="#first#"><i class="fa fa-step-backward"></i></a>' +
            '    </li>' +
            '    <li class="page-prev btn-nav">' +
            '        <a href="javascript:;" title="#prev#"><i class="fa fa-backward"></i></a>' +
            '    </li>' +
            '    #pageNumFrag#' +
            '    <li class="page-next btn-nav">' +
            '        <a href="javascript:;" title="#next#"><i class="fa fa-forward"></i></a>' +
            '    </li>' +
            '    <li class="page-last btn-nav">' +
            '        <a href="javascript:;" title="#last#"><i class="fa fa-step-forward"></i></a>' +
            '    </li>' +
            '    <li class="page-totalCount">' +
            '        <span title="#totalCount#">#totalCount#</span>' +
            '    </li>' +
            '</ul>'
        ,
        gridPageNum: '<li class="page-num#active#"><a href="javascript:;">#num#</a></li>',
        gridMenu: '<div class="datagrid-menu-box">'
            + '    <ul>'
            + '        <li class="datagrid-li-asc"><a href="javascript:;"><span class="icon"><i class="fa fa-sort-amount-asc"></i></span><span class="title">#asc#</span></a></li>'
            + '        <li class="datagrid-li-desc"><a href="javascript:;"><span class="icon"><i class="fa fa-sort-amount-desc"></i></span><span class="title">#desc#</span></a></li>'
            + '        <li class="datagrid-li-filter"><a href="javascript:;"><span class="icon"><i class="fa fa-filter"></i></span><span class="title">#filter#</span><span class="arrow"></span></a></li>'
            + '        <li class="datagrid-li-showhide"><a href="javascript:;"><span class="icon"><i class="fa fa-check-square-o"></i></span><span class="title">#showhide#</span><span class="arrow"></span></a></li>'
            + '        <li class="datagrid-li-lock"><a href="javascript:;"><span class="icon"><i class="fa fa-lock"></i></span><span class="title">#lock#</span></a></li>'
            + '        <li class="datagrid-li-unlock disable"><a href="javascript:;"><span class="icon"><i class="fa fa-unlock"></i></span><span class="title">#unlock#</span></a></li>'
            + '    </ul>'
            + '</div>'
        ,
        gridFilter: '<div class="datagrid-filter-box">'
            + '<fieldset>'
            + '<legend>#label#</legend>'
            + '<span class="filter-a"></span>'
            + '<span class="filter-and"><select data-toggle="selectpicker" data-container="true" data-width="100%"><option value="and">AND</option><option value="or">OR</option></select></span>'
            + '<span class="filter-b"></span>'
            + '<span class="filter-ok"><button type="button" class="btn-green ok" data-icon="check">#filter#</button><button type="button" class="btn-orange clear" data-icon="remove">#clear#</button></span>'
            + '</fieldset>'
            + '</div>'
        ,
        gridShowhide: '<li data-index="#index#" class="datagrid-col-check"><a href="javascript:;"><i class="fa fa-check-square-o"></i>#label#</a></li>',
        gridEditBtn: '<button type="button" class="btn btn-green bjui-datagrid-btn edit"><i class="fa fa-edit"></i> #edit#</button>'
            + '<button type="button" class="btn btn-green bjui-datagrid-btn update"><i class="fa fa-edit"></i> #update#</button>'
            + '<button type="button" class="btn btn-green bjui-datagrid-btn save"><i class="fa fa-check"></i> #save#</button>'
            + '<button type="button" class="btn btn-orange bjui-datagrid-btn cancel"><i class="fa fa-undo"></i> #cancel#</button>'
            + '<button type="button" class="btn btn-red bjui-datagrid-btn delete"><i class="fa fa-remove"></i> #del#</button>'
        ,
        gridDialogEditBtns: '<ul>'
            + '    <li class="pull-left"><button type="button" class="btn btn-orange prev" data-icon="arrow-up">#prev#</button></li>'
            + '    <li class="pull-left"><button type="button" class="btn btn-orange next" data-icon="arrow-down">#next#</button></li>'
            + '    <li><button type="button" class="btn btn-red cancel" data-icon="remove">#cancel#</button></li>'
            + '    <li><button type="button" class="btn btn-default save" data-icon="save">#save#</button></li>'
            + '</ul>'
        ,
        gridExpandBtn: '<span title="#expandMsg#"><i class="fa fa-plus"></i></span>',
        gridShrinkBtn: '<span title="#shrinkMsg#"><i class="fa fa-minus"></i></span>',
        alertBoxFrag: '<div id="bjui-alertMsgBox" class="bjui-alert"><div class="alertContent"><div class="#type#"><div class="alertInner"><h1><i class="fa #fa#"></i>#title#</h1><div class="msg">#message##prompt#</div></div><div class="toolBar clearfix"><ul>#btnFragment#</ul></div></div></div></div>',
        alertBtnFrag: '<li><button class="btn btn-#class#" rel="#callback#" type="button">#btnMsg#</button></li>',
        calendarFrag: '<div id="bjui-calendar">' +
            '    <div class="main">' +
            '        <a class="close" href="javascript:;" title="#close#"><i class="fa fa-times-circle"></i></a>' +
            '        <div class="head">' +
            '            <table width="100%" border="0" cellpadding="0" cellspacing="2">' +
            '                <tr>' +
            '                    <td width="20"><a class="prev" href="javascript:;" title="#prev#"><i class="fa fa-arrow-left"></i></a></td>' +
            '                    <td><select name="year"></select></td>' +
            '                    <td><select name="month"></select></td>' +
            '                    <td width="20"><a class="next" href="javascript:;" title="#next#"><i class="fa fa-arrow-right"></i></a></td>' +
            '                </tr>' +
            '            </table>' +
            '        </div>' +
            '        <div class="body">' +
            '            <dl class="dayNames"><dt>7</dt><dt>1</dt><dt>2</dt><dt>3</dt><dt>4</dt><dt>5</dt><dt>6</dt></dl>' +
            '            <dl class="days"><!-- date list --></dl>' +
            '            <div style="clear:both;height:0;line-height:0"></div>' +
            '        </div>' +
            '        <div class="foot">' +
            '            <table class="time">' +
            '                <tr>' +
            '                    <td>' +
            '                        <input type="text" class="hh" maxlength="2" data-type="hh" data-start="0" data-end="23">:<input' +
            '                         type="text" class="mm" maxlength="2" data-type="mm" data-start="0" data-end="59">:<input' +
            '                         type="text" class="ss" maxlength="2" data-type="ss" data-start="0" data-end="59">' +
            '                    </td>' +
            '                    <td><ul><li class="up" data-add="1">&and;</li><li class="down">&or;</li></ul></td>' +
            '                </tr>' +
            '            </table>' +
            '            <button type="button" class="clearBtn btn btn-orange">#clear#</button>' +
            '            <button type="button" class="okBtn btn btn-default">#ok#</button>' +
            '        </div>' +
            '        <div class="tm">' +
            '            <ul class="hh">' +
            '                <li>0</li>' +
            '                <li>1</li>' +
            '                <li>2</li>' +
            '                <li>3</li>' +
            '                <li>4</li>' +
            '                <li>5</li>' +
            '                <li>6</li>' +
            '                <li>7</li>' +
            '                <li>8</li>' +
            '                <li>9</li>' +
            '                <li>10</li>' +
            '                <li>11</li>' +
            '                <li>12</li>' +
            '                <li>13</li>' +
            '                <li>14</li>' +
            '                <li>15</li>' +
            '                <li>16</li>' +
            '                <li>17</li>' +
            '                <li>18</li>' +
            '                <li>19</li>' +
            '                <li>20</li>' +
            '                <li>21</li>' +
            '                <li>22</li>' +
            '                <li>23</li>' +
            '            </ul>' +
            '            <ul class="mm">' +
            '                <li>0</li>' +
            '                <li>5</li>' +
            '                <li>10</li>' +
            '                <li>15</li>' +
            '                <li>20</li>' +
            '                <li>25</li>' +
            '                <li>30</li>' +
            '                <li>35</li>' +
            '                <li>40</li>' +
            '                <li>45</li>' +
            '                <li>50</li>' +
            '                <li>55</li>' +
            '            </ul>' +
            '            <ul class="ss">' +
            '                <li>0</li>' +
            '                <li>10</li>' +
            '                <li>20</li>' +
            '                <li>30</li>' +
            '                <li>40</li>' +
            '                <li>50</li>' +
            '            </ul>' +
            '        </div>' +
            '    </div>' +
            '</div>'
        ,
        spinnerBtn: '<ul class="bjui-spinner"><li class="up" data-add="1">&and;</li><li class="down">&or;</li></ul>',
        findgridBtn: '<a class="bjui-lookup" href="javascript:;" data-toggle="findgridbtn"><i class="fa fa-search"></i></a>',
        dateBtn: '<a class="bjui-lookup" href="javascript:;" data-toggle="datepickerbtn"><i class="fa fa-calendar"></i></a>',
        navtabCM: '<ul id="bjui-navtabCM">' +
            '    <li rel="reload"><span class="icon"><i class="fa fa-refresh"></i></span><span class="title">#refresh#</span></li>' +
            '    <li rel="closeCurrent"><span class="icon"><i class="fa fa-remove"></i></span><span class="title">#close#</li>' +
            '    <li rel="closeOther"><span class="icon"><i class="fa fa-remove"></i></span><span class="title">#closeother#</li>' +
            '    <li rel="closeAll"><span class="icon"><i class="fa fa-remove"></i></span><span class="title">#closeall#</li>' +
            '</ul>'
        ,
        dialogCM: '<ul id="bjui-dialogCM">' +
            '    <li rel="reload"><span class="icon"><i class="fa fa-refresh"></i></span><span class="title">#refresh#</span></li>' +
            '    <li rel="closeCurrent"><span class="icon"><i class="fa fa-remove"></i></span><span class="title">#close#</span></li>' +
            '    <li rel="closeOther"><span class="icon"><i class="fa fa-remove"></i></span><span class="title">#closeother#</span></li>' +
            '    <li rel="closeAll"><span class="icon"><i class="fa fa-remove"></i></span><span class="title">#closeall#</span></li>' +
            '</ul>'
        ,
        externalFrag: '<iframe src="{url}" style="width:100%;height:{height};" frameborder="no" border="0" marginwidth="0" marginheight="0"></iframe>'
    }

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-extends.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-extends.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    $.fn.extend({
        /**
         * @param {Object} op: {type:GET/POST, url:ajax请求地址, data:ajax请求参数列表, callback:回调函数 }
         */
        ajaxUrl: function (op) {
            var $this = $(this), $target = $this//$this.closest('.bjui-pageContent')

            if (!$target.length)
                $target = $this

            $this.trigger(BJUI.eventType.beforeAjaxLoad)

            if (op.loadingmask) {
                $target.trigger(BJUI.eventType.ajaxStatus)
            }

            $.ajax({
                type: op.type || 'GET',
                url: op.url,
                data: op.data || {},
                cache: false,
                dataType: 'html',
                timeout: BJUI.ajaxTimeout,
                success: function (response) {
                    var json = response.toJson(), $ajaxMask = $target.find('> .bjui-ajax-mask')

                    if (!json[BJUI.keys.statusCode]) {
                        $this.empty().html(response).initui()
                    } else {
                        if (json[BJUI.keys.statusCode] == BJUI.statusCode.error) {
                            if (json[BJUI.keys.message]) $this.alertmsg('error', json[BJUI.keys.message])
                            if (!$this.closest('.bjui-layout').length) {
                                if ($this.closest('.navtab-panel').length) $this.navtab('closeCurrentTab')
                                else $this.dialog('closeCurrent')
                            }
                        } else if (json[BJUI.keys.statusCode] == BJUI.statusCode.timeout) {
                            if ($this.closest('.bjui-dialog').length) $this.dialog('closeCurrent')
                            if ($this.closest('.navtab-panel').length) $this.navtab('closeCurrentTab')

                            BJUI.alertmsg('info', (json[BJUI.keys.message] || BJUI.regional.sessiontimeout))
                            BJUI.loadLogin()
                        }
                        $ajaxMask.fadeOut('normal', function () {
                            $(this).remove()
                        })
                    }

                    if ($.isFunction(op.callback)) op.callback(response)
                },
                error: function (xhr, ajaxOptions, thrownError) {
                    $this.bjuiajax('ajaxError', xhr, ajaxOptions, thrownError)
                    if (!$this.closest('.bjui-layout').length) {
                        if ($this.closest('.navtab-panel').length) $this.navtab('closeCurrentTab')
                        else $this.dialog('closeCurrent')
                    }
                    $this.trigger('bjui.ajaxError')
                },
                statusCode: {
                    0: function (xhr, ajaxOptions, thrownError) {
                        BJUI.alertmsg('error', BJUI.regional.ajaxnosend)
                    },
                    503: function (xhr, ajaxOptions, thrownError) {
                        BJUI.alertmsg('error', BJUI.regional.statusCode_503)
                    }
                }
            })
        },
        loadUrl: function (url, data, callback) {
            $(this).ajaxUrl({ url: url, data: data, callback: callback })
        },
        doAjax: function (op) {
            var $this = $(this), $target, $ajaxMask

            $this.data('holdSubmit', true)

            if (!op.url) {
                BJUI.debug('The ajax url is undefined!')
                return
            }
            if (!op.callback) {
                BJUI.debug('The ajax callback is undefined!')
                return
            } else {
                op.callback = op.callback.toFunc()
            }
            if (op.loadingmask) {
                $target = $this.isTag('form') ? $this.closest('.bjui-pageContent') : $this

                if (!$target.length)
                    $target = $this

                $target.trigger(BJUI.eventType.ajaxStatus)
                $ajaxMask = $target.find('> .bjui-ajax-mask')
            }
            if (!op.type) op.type = 'POST'
            if (!op.dataType) op.dataType = 'json'
            if (!op.cache) op.cache = false
            op.timeout = op.ajaxTimeout || BJUI.ajaxTimeout
            op.success = function (response) {
                if ($ajaxMask) {
                    if (op.callback) {
                        $.when(op.callback(response)).done(function () {
                            $target.trigger('bjui.ajaxStop')
                        })
                    } else {
                        $target.trigger('bjui.ajaxStop')
                    }
                } else {
                    op.callback(response)
                }

                $this.data('holdSubmit', false)
            }
            op.error = function (xhr, ajaxOptions, thrownError) {
                if ($ajaxMask)
                    $target.trigger('bjui.ajaxError')

                $this.bjuiajax('ajaxError', xhr, ajaxOptions, thrownError)

                $this.data('holdSubmit', false)
            }
            op.statusCode = {
                0: function (xhr, ajaxOptions, thrownError) {
                    BJUI.alertmsg('error', BJUI.regional.ajaxnosend)

                    $this.data('holdSubmit', false)
                },
                503: function (xhr, ajaxOptions, thrownError) {
                    BJUI.alertmsg('error', BJUI.regional.statusCode_503)

                    $this.data('holdSubmit', false)
                }
            }

            $.ajax(op)
        },
        getPageTarget: function () {
            var $target

            if (this.closest('.bjui-layout').length) $target = this.closest('.bjui-layout')
            else if (this.closest('.navtab-panel').length) $target = $.CurrentNavtab
            else $target = $.CurrentDialog

            return $target
        },
        resizePageH: function () {
            return this.each(function () {
                if ($(this).closest('.tab-content').length) return

                var $box = $(this),
                    $pageHeader = $box.find('> .bjui-pageHeader'),
                    $pageContent = $box.find('> .bjui-pageContent'),
                    $pageFooter = $box.find('> .bjui-pageFooter'),
                    headH = $pageHeader.outerHeight() || 0,
                    footH = $pageFooter.outerHeight() || 0

                if ($pageFooter.css('bottom')) footH += parseInt($pageFooter.css('bottom')) || 0

                $pageContent.css({ top: headH, bottom: footH })

                if ($box.hasClass('dialogContent') && !$pageContent.length) {
                    $box.css({ padding: 10, overflow: 'auto' })
                }
            })
        },
        getMaxIndexObj: function ($elements) {
            var zIndex = 0, index = 0

            $elements.each(function (i) {
                var newZIndex = parseInt($(this).css('zIndex')) || 1

                if (zIndex < newZIndex) {
                    zIndex = newZIndex
                    index = i
                }
            })

            return $elements.eq(index)
        },
        /**
         * 将表单数据转成JSON对象 用法：$(form).serializeJson() Author: K'naan
         */
        serializeJson: function () {
            var o = {}
            var a = this.serializeArray()

            $.each(a, function () {
                if (o[this.name] !== undefined) {
                    if (!o[this.name].push) {
                        o[this.name] = [o[this.name]]
                    }
                    o[this.name].push(this.value || '')
                } else {
                    o[this.name] = this.value || ''
                }
            })

            return o
        },
        isTag: function (tn) {
            if (!tn) return false
            if (!$(this).prop('tagName')) return false
            return $(this)[0].tagName.toLowerCase() == tn ? true : false
        },
        /**
         * 判断当前元素是否已经绑定某个事件
         * @param {Object} type
         */
        isBind: function (type) {
            var _events = $(this).data('events')
            return _events && type && _events[type]
        },
        /**
         * 输出firebug日志
         * @param {Object} msg
         */
        log: function (msg) {
            return this.each(function () {
                if (console) console.log('%s: %o', msg, this)
            })
        }
    })

    /**
     * 扩展String方法
     */
    $.extend(String.prototype, {
        isPositiveInteger: function () {
            return (new RegExp(/^[1-9]\d*$/).test(this))
        },
        isInteger: function () {
            return (new RegExp(/^\d+$/).test(this))
        },
        isNumber: function () {
            return (new RegExp(/^([-]{0,1}(\d+)[\.]+(\d+))|([-]{0,1}(\d+))$/).test(this))
        },
        isNormalID: function () {
            return (new RegExp(/^[a-zA-Z][0-9a-zA-Z_-]*$/).test(this))
        },
        includeChinese: function () {
            return (new RegExp(/[\u4E00-\u9FA5]/).test(this))
        },
        trim: function () {
            return $.trim(this)
        },
        startsWith: function (pattern) {
            return this.indexOf(pattern) === 0
        },
        endsWith: function (pattern) {
            var d = this.length - pattern.length
            return d >= 0 && this.lastIndexOf(pattern) === d
        },
        replaceSuffix: function (index) {
            return this.replace(/\[[0-9]+\]/, '[' + index + ']').replace('#index#', index)
        },
        replaceSuffix2: function (index) {
            return this.replace(/\-(i)([0-9]+)$/, '-i' + index).replace('#index#', index)
        },
        trans: function () {
            return this.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"')
        },
        encodeTXT: function () {
            return (this).replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll(' ', '&nbsp;')
        },
        replaceAll: function (os, ns) {
            return this.replace(new RegExp(os, 'gm'), ns)
        },
        /*替换占位符为对应选择器的值*/ //{^(.|\#)[A-Za-z0-9_-\s]*}
        replacePlh: function ($box) {
            $box = $box || $(document)
            return this.replace(/{\/?[^}]*}/g, function ($1) {
                var $input = $box.find($1.replace(/[{}]+/g, ''))

                return $input && $input.val() ? $input.val() : $1
            })
        },
        replaceMsg: function (holder) {
            return this.replace(new RegExp('({.*})', 'g'), holder)
        },
        replaceTm: function ($data) {
            if (!$data) return this

            return this.replace(RegExp('({[A-Za-z_]+[A-Za-z0-9_-]*})', 'g'), function ($1) {
                return $data[$1.replace(/[{}]+/g, '')]
            })
        },
        replaceTmById: function (_box) {
            var $parent = _box || $(document)

            return this.replace(RegExp('({[A-Za-z_]+[A-Za-z0-9_-]*})', 'g'), function ($1) {
                var $input = $parent.find('#' + $1.replace(/[{}]+/g, ''))
                return $input.val() ? $input.val() : $1
            })
        },
        isFinishedTm: function () {
            return !(new RegExp('{\/?[^}]*}').test(this))
        },
        skipChar: function (ch) {
            if (!this || this.length === 0) return ''
            if (this.charAt(0) === ch) return this.substring(1).skipChar(ch)
            return this
        },
        isValidPwd: function () {
            return (new RegExp(/^([_]|[a-zA-Z0-9]){6,32}$/).test(this))
        },
        isValidMail: function () {
            return (new RegExp(/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/).test(this.trim()))
        },
        isSpaces: function () {
            for (var i = 0; i < this.length; i += 1) {
                var ch = this.charAt(i)

                if (ch != ' ' && ch != '\n' && ch != '\t' && ch != '\r') return false
            }
            return true
        },
        isPhone: function () {
            return (new RegExp(/(^([0-9]{3,4}[-])?\d{3,8}(-\d{1,6})?$)|(^\([0-9]{3,4}\)\d{3,8}(\(\d{1,6}\))?$)|(^\d{3,8}$)/).test(this))
        },
        isUrl: function () {
            return (new RegExp(/^[a-zA-z]+:\/\/([a-zA-Z0-9\-\.]+)([-\w .\/?%&=:]*)$/).test(this))
        },
        isExternalUrl: function () {
            return this.isUrl() && this.indexOf('://' + document.domain) == -1
        },
        toBool: function () {
            return (this.toLowerCase() === 'true') ? true : false
        },
        toJson: function () {
            var json = this

            try {
                if (typeof json === 'object') json = json.toString()
                if (!json.trim().match("^\{(.+:.+,*){1,}\}$")) return this
                else return JSON.parse(this)
            } catch (e) {
                return this
            }
        },
        toObj: function () {
            var obj = null

            try {
                obj = (new Function('return ' + this))()
            } catch (e) {
                obj = this
                BJUI.debug('String toObj：Parse "String" to "Object" error! Your str is: ' + this)
            }
            return obj
        },
        /**
         * String to Function
         * 参数(方法字符串或方法名)： 'function(){...}' 或 'getName' 或 'USER.getName' 均可
         * Author: K'naan
         */
        toFunc: function () {
            if (!this || !this.length) return undefined

            if (this.startsWith('function')) {
                return (new Function('return ' + this))()
            }

            try {
                var m_arr = this.split('.'), fn = window

                for (var i = 0; i < m_arr.length; i++) {
                    fn = fn[m_arr[i]]
                }

                if (typeof fn === 'function') {
                    return fn
                }
            } catch (e) {
                return undefined
            }
        }
    })

    /* Function */
    $.extend(Function.prototype, {
        //to fixed String.prototype -> toFunc
        toFunc: function () {
            return this
        }
    })

    /* Array */
    $.extend(Array.prototype, {
        remove: function (index) {
            if (index < 0) return this
            else return this.slice(0, index).concat(this.slice(index + 1, this.length))
        },
        swap: function (indexA, indexB) {
            var arr = this, temp = arr[indexA]

            arr[indexA] = arr[indexB]
            arr[indexB] = temp

            return this
        },
        move: function (old_index, new_index) {
            if (old_index < 0 || new_index < 0)
                return this

            if (new_index >= this.length) {
                var k = new_index - this.length

                while ((k--) + 1) {
                    this.push(undefined)
                }
            }

            this.splice(new_index, 0, this.splice(old_index, 1)[0])

            return this
        },
        // move continuous item
        moveItems: function (startIndex, newIndex, len) {
            if (!len || newIndex == startIndex) return this

            var moveArr = this.slice(startIndex, startIndex + len)

            if (startIndex > newIndex)
                this.splice(startIndex, len)

            this.splice.apply(this, [newIndex, 0].concat(moveArr))

            if (startIndex < newIndex)
                this.splice(startIndex, len)

            return this
        },
        unique: function () {
            var temp = new Array()

            this.sort()
            for (var i = 0; i < this.length; i++) {
                if (this[i] == this[i + 1]) continue
                temp[temp.length] = this[i]
            }

            return temp
        },
        myIndexOf: function (e) {
            if (!this || !this.length) return -1

            for (var i = 0, j; j = this[i]; i++) {
                if (j == e) return i
            }

            return -1
        },
        /* serializeArray to json */
        toJson: function () {
            var o = {}
            var a = this

            $.each(a, function () {
                if (o[this.name] !== undefined) {
                    if (!o[this.name].push) {
                        o[this.name] = [o[this.name]]
                    }
                    o[this.name].push(this.value || '')
                } else {
                    o[this.name] = this.value || ''
                }
            })

            return o
        }
    })

    /* Global */
    $.isJson = function (obj) {
        var flag = true

        try {
            flag = $.parseJSON(obj)
        } catch (e) {
            return false
        }
        return flag ? true : false
    }

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-basedrag.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-basedrag.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // BASEDRAG CLASS DEFINITION
    // ======================

    var Basedrag = function (element, options) {
        this.$element = $(element)
        this.options = options
        this.tools = this.TOOLS()
    }

    Basedrag.prototype.TOOLS = function () {
        var tools = {
            isOver: function ($drop, x, y) {
                var offset = $drop.offset(),
                    left = offset.left,
                    top = offset.top,
                    width = $drop.outerWidth(),
                    height = $drop.outerHeight()

                return (x > left && x < (left + width)) && (y > top && y < (top + height))
            },
            mouseDirection: function (element, opts) {
                var $element = $(element), dirs = ['top', 'right', 'bottom', 'left']
                var calculate = function (e) {
                    var w = $element.outerWidth(),
                        h = $element.outerHeight(),
                        offset = $element.offset(),
                        x = (e.pageX - offset.left - (w / 2)) * (w > h ? (h / w) : 1),
                        y = (e.pageY - offset.top - (h / 2)) * (h > w ? (w / h) : 1)

                    return Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180) / 90) + 3) % 4
                }

                //enter leave代表鼠标移入移出时的回调
                opts = $.extend({}, {
                    move: $.noop
                }, opts || {})

                var r = calculate(opts.e)

                opts.move($element, dirs[r])
                /*
                $element.off('mouseenter.basedrag.direction').on('mouseenter.basedrag.direction', function (e) {
                    var r = calculate(e)
                    
                    opts.enter($element, dirs[r])
                }).off('mouseleave.basedrag.direction').on('mouseleave.basedrag.direction', function (e) {
                    var r = calculate(e)
                    
                    opts.leave($element, dirs[r])
                })*/
            }
        }

        return tools
    }

    Basedrag.prototype.init = function () {
        var that = this

        that.$drag = this.$element
        that.options.$obj = this.$element
        if (that.options.obj) this.options.$obj = this.options.obj
        if (that.options.event) {
            that.hasEvent = true
            that.start(this.options.event)
        } else {
            if (that.options.selector)
                that.$drag = that.$element.find(that.options.selector)

            if (that.$drag.length) {
                that.$drag.on('mousedown', function (e) {
                    that.start.apply(that, [e])
                    that.options.event = e
                })
            }
        }
    }

    Basedrag.prototype.start = function (e) {
        $(document).on('selectstart', false)
        var that = this, options = that.options

        if (options.exclude) {
            var $exclude = that.$element.find(options.exclude)

            if ($exclude.length && $.inArray(e.target, $exclude.add($exclude.find('*'))) != -1) {
                return false
            }
        }

        if (this.$element.css('position') === 'static') {
            this.$element.css('position', 'absolute')
            !options.oleft && (options.oleft = this.$element.position().left)
            !options.otop && (options.otop = this.$element.position().top)
            this.$element.css('position', '')
        }
        if (!options.oleft) options.oleft = parseInt(this.$element.css('left')) || 0
        if (!options.otop) options.otop = parseInt(this.$element.css('top')) || 0

        $(document)
            .on('mouseup.bjui.basedrag', function (e) { that.stop.apply(that, [e]) })
            .on('mousemove.bjui.basedrag', function (e) { that.drag.apply(that, [e]) })
    }

    Basedrag.prototype.drop = function (e) {
        var that = this, options = that.options, $drop = options.drop

        if (!$drop instanceof jQuery)
            $drop = $($drop)

        if ($drop.length) {
            that.$drops = $drop
            $drop.each(function () {
                if (that.tools.isOver($(this), e.pageX, e.pageY)) {
                    $(this).trigger('dropover.bjui.basedrag', [e.pageX, e.pageY, that.$element])

                    that.isDrop = true
                    that.$drop = $(this)

                    return false
                } else {
                    $(this).trigger('dropout.bjui.basedrag', [e.pageX, e.pageY, that.$element])
                    that.isDrop = false
                }
            }).css('-moz-user-select', 'none')
        }
    }

    Basedrag.prototype.drag = function (e) {
        if (!e) e = window.event

        BJUI['bjui.basedrag'] = this.$element

        var that = this,
            options = this.options,
            beforeDrag = options.beforeDrag,
            trData = options.treeData,      // only for drop Datagrid
            dataIndex = this.$element.index(), // only for drop Datagrid
            left = options.oleft + (e.pageX - options.event.pageX),
            top = options.otop + (e.pageY - options.event.pageY)

        if (beforeDrag && typeof beforeDrag === 'string')
            beforeDrag = beforeDrag.toFunc()
        if (beforeDrag && typeof beforeDrag === 'function') {
            if (this.$element.next().hasClass('datagrid-child-tr'))
                dataIndex = dataIndex / 2

            if (!beforeDrag.apply(this, [this.$element, (trData ? trData[dataIndex] : '')])) {
                return false
            }
        }

        if (options.drop) options.move = ''
        //if (top < 1) top = 0
        if (options.move === 'horizontal') {
            if ((options.minW && left >= parseInt(this.options.$obj.css('left')) + options.minW) && (options.maxW && left <= parseInt(this.options.$obj.css('left')) + options.maxW)) {
                this.$element.css('left', left)
            } else if (options.scop) {
                if (options.relObj) {
                    if ((left - parseInt(options.relObj.css('left'))) > options.cellMinW)
                        this.$element.css('left', left)
                    else
                        this.$element.css('left', left)
                }
            }
        } else if (options.move === 'vertical') {
            this.$element.css('top', top)
        } else {
            var $selector = options.selector ? this.options.$obj.find(options.selector) : this.options.$obj

            if (options.drop) {
                var $placeholder = $('#bjui-drag-placeholder')

                if (!$placeholder.length)
                    $placeholder = $('<div style="position:absolute; z-index:999;" id="bjui-drag-placeholder"></div>').appendTo($('body'))

                $placeholder.css({ left: e.pageX, top: e.pageY, opacity: 1 }).show().empty().append(this.$element.clone())

                this.$placeholder = $placeholder
            } else {
                this.$element.css({ left: left, top: top })
            }
        }
        if (options.drag)
            options.drag.apply(this.$element, [this.$element, e, left, top])

        if (options.drop) {
            that.drop(e)
        }
        if (options.container) {
            if (!options.container instanceof jQuery)
                options.container = $(options.container)

            var scrollTop = options.container.scrollTop()

            if (!scrollTop) {
                options.container.scrollTop(1)
            }
            if (options.container.scrollTop()) {
                that.tools.mouseDirection(options.container, {
                    e: e,
                    move: function ($element, dir) {
                        if (dir === 'top' || dir === 'bottom') {
                            options.container.scrollTop(options.container.scrollTop() + (e.clientY - options.event.clientY) / 13)
                        }
                    }
                })
            }
        }

        return this.preventEvent(e)
    }

    Basedrag.prototype.stop = function (e) {
        var that = this

        $(document).off('mousemove.bjui.basedrag').off('mouseup.bjui.basedrag').off('mouseover.bjui.basedrag')

        that.options.drop && that.options.drop.off('mouseenter.bjui.basedrag')

        if (this.options.stop)
            this.options.stop.apply(this.$element, [this.$element, e])

        if (this.hasEvent)
            this.destroy()
        else {
            that.$drag.off('mousedown').on('mousedown', $.proxy(function (e) {
                that.options.event = e
                that.options.oleft = 0
                that.options.otop = 0

                that.start(e)
            }, that))
        }

        if (that.options.drop && !that.isDrop) {
            var offset = that.$element.offset()

            that.$placeholder && that.$placeholder.animate({ top: offset.top, left: offset.left, opacity: 0.2 }, 'normal', function () {
                that.$placeholder.hide()
            })
        }
        if (that.isDrop) {
            that.$placeholder && that.$placeholder.hide()
            that.$drop && that.$drop.trigger('drop.bjui.basedrag', [that.$element])
        }

        that.$drops && that.$drops.css('-moz-user-select', '')
        $(document).off('selectstart')
        return this.preventEvent(e)
    }

    Basedrag.prototype.preventEvent = function (e) {
        if (e.stopPropagation) e.stopPropagation()
        if (e.preventDefault) e.preventDefault()
        return false
    }

    Basedrag.prototype.destroy = function () {
        this.$element.removeData('bjui.basedrag')
        if (!this.options.nounbind) this.$element.off('mousedown')
    }

    // BASEDRAG PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments
        var property = option

        return this.each(function () {
            var $this = $(this)
            var options = $.extend({}, $this.data(), typeof option === 'object' && option)
            var data = $this.data('bjui.basedrag')

            if (!data) $this.data('bjui.basedrag', (data = new Basedrag(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.basedrag

    $.fn.basedrag = Plugin
    $.fn.basedrag.Constructor = Basedrag

    // BASEDRAG NO CONFLICT
    // =================

    $.fn.basedrag.noConflict = function () {
        $.fn.basedrag = old
        return this
    }

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-sidenav.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-sidenav.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // SLIDEBAR CLASS DEFINITION
    // ======================

    var Sidenav = function (element, options) {
        this.$element = $(element)
        this.options = options
    }

    Sidenav.prototype.ajaxHnav = function () {
        var that = this, options = that.options, childKey = options.childKey || 'children', $li = that.$element.parent(), $box = $('#bjui-sidenav-box'), html = BJUI.StrBuilder(),
            $sidenavbtn = $('#bjui-sidenav-btn')
        var createA = function (data) {
            var liHtml = BJUI.StrBuilder(), target = data.target || 'navtab'

            liHtml.add('<a href="' + (data.url || 'javascript:;') + '" ')

            if (data.url && (target === 'dialog' || target === 'navtab')) {
                liHtml.add('data-toggle="' + target + '" data-options="{id:\'' + data.id + '\', title:\'' + (data.title || data.name) + '\'')

                if (data.width) liHtml.add(', width:' + data.width)
                if (data.height) liHtml.add(', height:' + data.height)
                if (data.fresh) liHtml.add(', fresh:' + data.fresh)
                if (data.mask) liHtml.add(', mask:' + data.mask)

                liHtml.add('}"')
            } else if (data.url)
                liHtml.add('target="' + target + '"')

            if (data[childKey])
                liHtml.add(' class="right-arrow"')

            liHtml.add('><i class="fa fa-' + (data.icon || 'caret-right') + '"></i>&nbsp;' + data.name)

            if (data[childKey])
                liHtml.add('<b><i class="fa fa-angle-right"></i></b>')

            liHtml
                .add('</a>')
                .add(createChild(data))

            return liHtml.toString()
        }
        var createChild = function (data) {
            var childHtml = BJUI.StrBuilder()

            if (data[childKey]) {
                childHtml.add('<ul class="nav">')

                $.each(data[childKey], function (i, m) {
                    var target = m.target || 'navtab', id = m.id && String(m.id).isNormalID && m.id

                    childHtml.add('<li')

                    if (target === 'navtab' && id && m.url)
                        childHtml.add(' class="' + target + '-' + id + '"')

                    childHtml
                        .add('>')
                        .add(createA(m))
                        .add('</li>')
                })

                childHtml.add('</ul>')
            }

            return childHtml.toString()
        }
        var okCallback = function (json) {
            if (!$.isArray(json)) {
                json = [json]
            }

            html.add('<ul class="nav">')

            $.each(json, function (i, n) {
                var target = n.target || 'navtab', id = n.id && String(n.id).isNormalID && n.id

                html.add('<li')

                if (target === 'navtab' && id && n.url)
                    html.add(' class="' + target + '-' + id + '"')

                html.add('>')
                    .add(createA(n))
                    .add('</li>')
            })

            html.add('</ul>')

            $box.html(html.toString())
                .off('click').on('click', '.nav > li > a', function (e) {
                    var $this = $(this), $nav = $this.next('.nav'), $li = $this.closest('li'), $other = $li.siblings()

                    if ($nav.length) {
                        $nav.stop().slideToggle(function () {
                            $li.toggleClass('open', $nav.is(':visible'))
                        })

                        if (!$li.hasClass('open')) {
                            $other.find('> .nav').stop().slideUp(function () {
                                $(this).closest('li').removeClass('open')
                            })
                        }
                    }
                })

            $li.data('bjui.sidenav.hnav.panels', $box.find('> ul'))

            $(document).on('switch.bjui.navtab', function (e) {
                var datas = $('body').data('bjui.navtab'), $lis

                if (datas && datas.current) {
                    $box.find('li').removeClass('active').filter('.navtab-' + datas.current).addClass('active')
                }
            })

            if (BJUI.ui.displayFirst)
                $box.find('> .nav > li:first > a').trigger('click')

            !$('body').data('bjui.sidenav.init') && $sidenavbtn.is(':visible') && ($sidenavbtn.trigger('click'))
        }
        var treeCallback = function (json) {
            var single = (typeof options.single === 'undefined' || single), $ul, html = BJUI.StrBuilder()

            $box.empty()

            if (single) {
                $ul =
                    $('<ul class="ztree" data-toggle="ztree" id="bjui-sidenav-ztree-0"></ul>')
                        .data('options', options.treeOptions)
                        .data('nodes', json)

                $box.append($ul).initui()
            } else {
                if (!$.isArray(json))
                    json = [json]

                $.each(json, function (i, n) {
                    $ul =
                        $('<ul class="ztree" data-toggle="ztree" id="bjui-sidenav-ztree-' + i + '"></ul>')
                            .data('options', options.treeOptions)
                            .data('nodes', json)

                    $box.append($ul)
                })

                $box.initui()
            }

            !$('body').data('bjui.sidenav.init') && $sidenavbtn.is(':visible') && ($sidenavbtn.trigger('click'))
        }

        BJUI.ajax('doajax', {
            url: options.url,
            loadingmask: false,
            target: $box,
            okCallback: options.tree ? treeCallback : okCallback
        })
    }

    Sidenav.prototype.initHnav = function () {
        var that = this, options = that.options, childKey = options.childKey || 'children', targetKey = options.targetKey || 'target', idKey = options.idKey || 'id',
            $li = that.$element.parent(), $box = $('#bjui-sidenav-box'), $sidenavbtn = $('#bjui-sidenav-btn'),
            html = BJUI.StrBuilder(), json
        var createA = function (data) {
            if (!data) data = {}

            var liHtml = BJUI.StrBuilder(), target = data[targetKey] || 'navtab'

            liHtml.add('<a href="' + (data.url || 'javascript:;') + '" ')

            if (data.url && (target === 'dialog' || target === 'navtab')) {
                liHtml.add('data-toggle="' + target + '" data-options="{id:\'' + data[idKey] + '\', title:\'' + (data.title || data.name) + '\'')

                if (data.width) liHtml.add(', width:' + data.width)
                if (data.height) liHtml.add(', height:' + data.height)
                if (data.fresh) liHtml.add(', fresh:' + data.fresh)
                if (data.mask) liHtml.add(', mask:' + data.mask)

                liHtml.add('}"')
            } else if (data.url)
                liHtml.add('target="' + target + '"')

            if (data[childKey])
                liHtml.add(' class="right-arrow"')

            liHtml.add('><i class="fa fa-' + (data.icon || 'caret-right') + '"></i>&nbsp;' + data.name)

            if (data[childKey])
                liHtml.add('<b><i class="fa fa-angle-right"></i></b>')

            liHtml
                .add('</a>')
                .add(createChild(data))

            return liHtml.toString()
        }

        var createChild = function (data) {
            var childHtml = BJUI.StrBuilder()

            if (data[childKey]) {
                childHtml.add('<ul class="nav">')

                $.each(data[childKey], function (i, m) {
                    var target = m.target || 'navtab', id = m[idKey] && String(m[idKey]).isNormalID && m[idKey]

                    childHtml.add('<li')

                    if (target === 'navtab' && id && m.url)
                        childHtml.add(' class="' + target + '-' + id + '"')

                    childHtml
                        .add('>')
                        .add(createA(m))
                        .add('</li>')
                })

                childHtml.add('</ul>')
            }

            return childHtml.toString()
        }
        var okCallback = function (json) {
            if (!$.isArray(json)) {
                json = [json]
            }

            html.add('<ul class="nav">')

            $.each(json, function (i, n) {
                var target = n.target || 'navtab', id = n[idKey] && String(n[idKey]).isNormalID && n[idKey]

                html.add('<li')

                if (target === 'navtab' && id && n.url)
                    html.add(' class="' + target + '-' + id + '"')

                html.add('>')
                    .add(createA(n))
                    .add('</li>')
            })

            html.add('</ul>')

            $box.html(html.toString())
                .off('click').on('click', '.nav > li > a', function (e) {
                    var $this = $(this), $nav = $this.next('.nav'), $li = $this.closest('li'), $other = $li.siblings()

                    if ($nav.length) {
                        $nav.stop().slideToggle(function () {
                            $li.toggleClass('open', $nav.is(':visible'))
                        })

                        if (!$li.hasClass('open')) {
                            $other.find('> .nav').stop().slideUp(function () {
                                $(this).closest('li').removeClass('open')
                            })
                        }
                    }
                })

            $li.data('bjui.sidenav.hnav.panels', $box.find('> ul'))

            $(document).on('switch.bjui.navtab', function (e) {
                var datas = $('body').data('bjui.navtab'), $lis

                if (datas && datas.current) {
                    $box.find('li').removeClass('active').filter('.navtab-' + datas.current).addClass('active')
                }
            })

            if (BJUI.ui.displayFirst)
                $box.find('> .nav > li:first > a').trigger('click')

            !$('body').data('bjui.sidenav.init') && $sidenavbtn.is(':visible') && ($sidenavbtn.trigger('click'))
        }
        var treeCallback = function (json) {
            var single = (typeof options.single === 'undefined' || single), $ul, html = BJUI.StrBuilder()

            $box.empty()

            if (single) {
                $ul =
                    $('<ul class="ztree" data-toggle="ztree" id="bjui-sidenav-ztree-0"></ul>')
                        .data('options', options.treeOptions)
                        .data('nodes', json)

                $box.append($ul).initui()
            } else {
                if (!$.isArray(json))
                    json = [json]

                $.each(json, function (i, n) {
                    $ul =
                        $('<ul class="ztree" data-toggle="ztree" id="bjui-sidenav-ztree-' + i + '"></ul>')
                            .data('options', options.treeOptions)
                            .data('nodes', json)

                    $box.append($ul)
                })

                $box.initui()
            }

            !$('body').data('bjui.sidenav.init') && $sidenavbtn.is(':visible') && ($sidenavbtn.trigger('click'))
        }

        json = that.$element.next('.items').html()
        if (json) {
            json = $.parseJSON(json)
            options.tree ? treeCallback(json) : okCallback(json)
        }
    }

    // SLIDEBAR PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments
        var property = option

        return this.each(function () {
            var $this = $(this)
            var options = $.extend({}, $this.data(), typeof option === 'object' && option)
            var data = $this.data('bjui.sidenav')

            if (!data) $this.data('bjui.sidenav', (data = new Sidenav(this, options)))

            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            }
        })
    }

    var old = $.fn.sidenav

    $.fn.sidenav = Plugin
    $.fn.sidenav.Constructor = Sidenav

    // SLIDEBAR NO CONFLICT
    // =================

    $.fn.basedrag.noConflict = function () {
        $.fn.sidenav = old
        return this
    }

    // SLIDEBAR DATA-API
    // ==============
    $(function () {
        $('#bjui-sidenav-col').on('click.bjui.sidenav', '.nav > li > a', function () {
            var $this = $('#bjui-sidenav-col')

            if ($(this).hasClass('right-arrow'))
                return false

            if ($this.hasClass('autohide'))
                $('#bjui-sidenav-arrow').trigger('click')
        })
    })

    $(document).on('click.bjui.sidenav.data-api', '[data-toggle="sidenav"]', function (e) {
        e.preventDefault()

        var $body = $('body'), $this = $(this), href = $this.data('url') || $this.attr('href'), cache = $this.data('cache'),
            $li = $(this).parent(), $box = $('#bjui-sidenav-box'), $panels = $li.data('bjui.sidenav.hnav.panels')

        if (typeof $body.data('bjui.sidenav.init') === 'undefined')
            $body.data('bjui.sidenav.init', true)
        else
            $body.data('bjui.sidenav.init', false)

        $li.addClass('active').siblings().removeClass('active')

        if (href && !(href.startsWith('#') || href.startsWith('javascript'))) {
            if (cache && $panels && $panels.length) {
                $box.append($panels)
            } else {
                $this.data('url', href)
                Plugin.call($this, 'ajaxHnav')
            }
        } else {
            $box.find('> .nav').detach()

            if ($panels && $panels.length) {
                $box.append($panels)
            } else {
                Plugin.call($this, 'initHnav')
            }
        }
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-contextmenu.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-contextmenu.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // CONTEXTMENU GLOBAL ELEMENTS
    // ======================

    var $menu, $shadow, hash

    $(function () {
        var INIT_CONTEXTMENU = function () {
            $menu = $('<div id="bjui-contextmenu"></div>').hide()
            $shadow = $('<div id="bjui-contextmenuShadow"></div>').hide()
            hash = []

            $('body').append('<!-- contextmenu -->').append($menu).append($shadow)
        }

        INIT_CONTEXTMENU()
    })

    // CONTEXTMENU CLASS DEFINITION
    // ======================
    var Contextmenu = function (element, options) {
        this.$element = $(element)
        this.options = options
    }

    Contextmenu.DEFAULTS = {
        id: undefined,
        shadow: true,
        bindings: {},
        ctrSub: null
    }

    Contextmenu.prototype.init = function () {
        var that = this
        var op = this.options

        if (!op.id) return
        hash.push({
            id: op.id,
            shadow: op.shadow,
            bindings: op.bindings || {},
            ctrSub: op.ctrSub
        })

        var index = hash.length - 1

        this.$element.on('contextmenu', function (e) {
            that.display(index, this, e, op)
            return false
        })
    }

    Contextmenu.prototype.display = function (index, trigger, e, options) {
        var that = this
        var cur = hash[index]
        var cp = BJUI.regional[cur.id]
        var content = FRAG[cur.id]

        $.each(cp, function (i, n) {
            content = content.replace('#' + i + '#', cp[i])
        })

        // Send the content to the menu
        $menu.html(content)
        $.each(cur.bindings, function (id, func) {
            $('[rel="' + id + '"]', $menu).on('click', function (e) {
                that.hide()
                func($(trigger), $('#bjui-' + cur.id))
            })
        })

        var posX = e.pageX
        var posY = e.pageY

        if ($(window).width() < posX + $menu.width()) posX -= $menu.width()
        if ($(window).height() < posY + $menu.height()) posY -= $menu.height()

        $menu.css({ 'left': posX, 'top': posY }).show()
        if (cur.shadow)
            $shadow.css({ width: $menu.width(), height: $menu.height(), left: posX + 3, top: posY + 3 }).show()
        $(document).one('click', that.hide)

        if ($.isFunction(cur.ctrSub))
            cur.ctrSub($(trigger), $('#bjui-' + cur.id))
    }

    Contextmenu.prototype.hide = function () {
        $menu.hide()
        $shadow.hide()
    }

    /* Custom contextmenu */
    Contextmenu.prototype.show = function (options) {
        var that = this

        if (options.items && options.items.length) {
            that.$element.on('contextmenu', function (e) {
                var isShow = true

                /*exclude*/
                if (options.exclude) {
                    that.$element.find(options.exclude).each(function () {
                        if (this == e.target || $(this).find(e.target).length) {
                            isShow = false
                            return
                        }
                    })
                }

                if (!isShow) {
                    e.stopPropagation()
                    return !isShow
                } else {
                    that.custom(options.items, e)
                }

                return false
            })
        }
    }

    Contextmenu.prototype.custom = function (items, e) {
        $menu.empty().html('<ul></ul>')

        var that = this
        var options = that.options
        var $ul = $menu.find('> ul'), $li

        $.each(items, function (i, n) {
            var icon = ''

            if (n.icon) icon = '<i class="fa fa-' + n.icon + '"></i>'
            if (n.title == 'diver') {
                $li = $('<li class="diver"></li>')
            } else {
                $li = $('<li><span class="icon">' + icon + '</span><span class="title">' + n.title + '</span></li>')
                if (n.func && typeof n.func === 'string') n.func = n.func.toFunc()
                if (n.func) {
                    $li.on('click', function (evt) {
                        that.hide()
                        n.func(that.$element, $li)
                    })
                }
            }
            $li.appendTo($ul)
        })

        var posX = e.pageX
        var posY = e.pageY

        if ($(window).width() < posX + $menu.width()) posX -= $menu.width()
        if ($(window).height() < posY + $menu.height()) posY -= $menu.height()

        $menu.css({ 'left': posX, 'top': posY }).show()

        if (options.shadow)
            $shadow.css({ width: $menu.width(), height: $menu.height(), left: posX + 3, top: posY + 3 }).show()

        $(document).one('click', that.hide)
    }

    // CONTEXTMENU PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments
        var property = option

        return this.each(function () {
            var $this = $(this)
            var options = $.extend({}, Contextmenu.DEFAULTS, $this.data(), typeof option === 'object' && option)
            var data = $this.data('bjui.contextmenu')

            if (!data) $this.data('bjui.contextmenu', (data = new Contextmenu(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.contextmenu

    $.fn.contextmenu = Plugin
    $.fn.contextmenu.Constructor = Contextmenu

    // CONTEXTMENU NO CONFLICT
    // =================

    $.fn.contextmenu.noConflict = function () {
        $.fn.contextmenu = old
        return this
    }

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-navtab.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-navtab.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // NAVTAB GLOBAL ELEMENTS
    // ======================

    var currentIndex, $currentTab, $currentPanel, $box, $tabs, $panels, $prevBtn, $nextBtn, $moreBtn, $moreBox, $main, $mainLi,
        autorefreshTimer

    $(function () {
        var INIT_NAVTAB = function () {
            currentIndex = 0
            $box = $('#bjui-navtab')
            $tabs = $box.find('> .tabsPageHeader > .tabsPageHeaderContent > .navtab-tab')
            $panels = $box.find('> .navtab-panel')
            $prevBtn = $box.find('> .tabsPageHeader > .tabsLeft')
            $nextBtn = $box.find('> .tabsPageHeader > .tabsRight')
            $moreBtn = $box.find('> .tabsPageHeader > .tabsMore')
            $moreBox = $box.find('> .tabsMoreList')
            $main = $tabs.find('li:first')
            $mainLi = $moreBox.find('li:first')

            $prevBtn.click(function () { $(this).navtab('scrollPrev') })
            $nextBtn.click(function () { $(this).navtab('scrollNext') })
            $moreBtn.click(function () {
                $moreBox.show()

                var hh = $(window).height(), mh = $moreBox.height(), th = $('#bjui-top').height() + $('#bjui-navbar').height(), sh = hh - th - 26

                if (sh < mh)
                    $moreBox.height(sh - 10)
                else
                    $moreBox.height('')
            })

            $(document).on('click.bjui.navtab.switchtab', function (e) {
                var $target = e.target.tagName == 'I' ? $(e.target).parent() : $(e.target)

                if ($moreBtn[0] != $target[0]) $moreBox.hide()
            })

            var mainTit, options = $.extend({}, Navtab.DEFAULTS, $main.data(), { id: 'main', title: mainTit }), mainTab = new Navtab(options)

            $('body').data('bjui.navtab', { main: mainTab, current: 'main' })

            if ($main.attr('data-url')) {
                $(document).one(BJUI.eventType.initUI, function (e) {
                    $main.removeAttr('data-url').navtab('reload', options)
                })
            }

            $main
                .data('options', $.extend({}, options))
                .navtab('contextmenu', $main)
                .click(function () { if (!$(this).hasClass('active')) BJUI.navtab('switchTab', 'main') })
                .find('> a > span').html(function (n, c) { return (mainTit = c.replace('#maintab#', BJUI.regional.maintab)) })

            if ($main.attr('data-url')) {
                $(document).one(BJUI.eventType.initUI, function (e) {
                    $main.removeAttr('data-url').navtab('reload', options)
                })
            }

            setTimeout(function () {
                $main.trigger('click')
            }, 100)

            $mainLi
                .click(function () {
                    if ($(this).hasClass('active')) $moreBox.hide()
                    else BJUI.navtab('switchTab', 'main')
                })
                .find('> a').html(function (n, c) { return c.replace('#maintab#', BJUI.regional.maintab) })

            $.CurrentNavtab = $panels.find('> .navtabPage').eq(0)
        }

        INIT_NAVTAB()
    })

    // NAVTAB CLASS DEFINITION
    // ======================

    var Navtab = function (options) {
        this.options = options
        this.tools = this.TOOLS()
    }

    Navtab.DEFAULTS = {
        id: null,
        title: 'New tab',
        url: null,
        type: 'GET',
        data: {},
        loadingmask: true,
        fresh: false,
        autorefresh: false,
        onLoad: null,
        beforeClose: null,
        onClose: null
    }

    Navtab.prototype.TOOLS = function () {
        var that = this
        var tools = {
            getDefaults: function () {
                return Navtab.DEFAULTS
            },
            getTabs: function () {
                return $tabs.find('> li')
            },
            getPanels: function () {
                return $panels.find('> div')
            },
            getMoreLi: function () {
                return $moreBox.find('> li')
            },
            getTab: function (tabid) {
                var index = this.indexTabId(tabid)

                if (index >= 0) return this.getTabs().eq(index)
            },
            getPanel: function (tabid) {
                var index = this.indexTabId(tabid)

                if (index >= 0) return this.getPanels().eq(index)
            },
            getTabsW: function (iStart, iEnd) {
                return this.tabsW(this.getTabs().slice(iStart, iEnd))
            },
            tabsW: function ($tabs) {
                var iW = 0

                $tabs.each(function () {
                    iW += $(this).outerWidth(true)
                })

                return iW
            },
            indexTabId: function (tabid) {
                var iOpenIndex = -1

                if (!tabid) return iOpenIndex

                this.getTabs().each(function (index) {
                    if ($(this).data('options').id === tabid) {
                        iOpenIndex = index
                        return false
                    }
                })

                return iOpenIndex
            },
            getLeft: function () {
                return $tabs.position().left
            },
            getScrollBarW: function () {
                return $box.width() - 55
            },
            visibleStart: function () {
                var $tabs = this.getTabs(), iLeft = this.getLeft(), iW = 0, index = 0

                $tabs.each(function (i) {
                    if (iW + iLeft >= 0) {
                        index = i
                        return false
                    }
                    iW += $(this).outerWidth(true)
                })

                return index
            },
            visibleEnd: function () {
                var tools = this, $tabs = this.getTabs(), iLeft = this.getLeft(), iW = 0, index = $tabs.length

                $tabs.each(function (i) {
                    iW += $(this).outerWidth(true)
                    if (iW + iLeft > tools.getScrollBarW()) {
                        index = i
                        return false
                    }
                })

                return index
            },
            scrollPrev: function () {
                var iStart = this.visibleStart()

                if (iStart > 0)
                    this.scrollTab(- this.getTabsW(0, iStart - 1))
            },
            scrollNext: function () {
                var iEnd = this.visibleEnd()

                if (iEnd < this.getTabs().size())
                    this.scrollTab(- this.getTabsW(0, iEnd + 1) + this.getScrollBarW())
            },
            scrollTab: function (iLeft, isNext) {
                $tabs.animate({ left: iLeft }, 150, function () { that.tools.ctrlScrollBtn() })
            },
            scrollCurrent: function () { // auto scroll current tab
                var iW = this.tabsW(this.getTabs()), scrollW = this.getScrollBarW()

                if (iW <= scrollW)
                    this.scrollTab(0)
                else if (this.getLeft() < scrollW - iW)
                    this.scrollTab(scrollW - iW)
                else if (currentIndex < this.visibleStart())
                    this.scrollTab(- this.getTabsW(0, currentIndex))
                else if (currentIndex >= this.visibleEnd())
                    this.scrollTab(scrollW - this.getTabs().eq(currentIndex).outerWidth(true) - this.getTabsW(0, currentIndex))
            },
            ctrlScrollBtn: function () {
                var iW = this.tabsW(this.getTabs())

                if (this.getScrollBarW() > iW) {
                    $prevBtn.hide()
                    $nextBtn.hide()
                    $tabs.parent().removeClass('tabsPageHeaderMargin')
                } else {
                    $prevBtn.show().removeClass('tabsLeftDisabled')
                    $nextBtn.show().removeClass('tabsRightDisabled')
                    $tabs.parent().addClass('tabsPageHeaderMargin')
                    if (this.getLeft() >= 0)
                        $prevBtn.addClass('tabsLeftDisabled')
                    else if (this.getLeft() <= this.getScrollBarW() - iW)
                        $nextBtn.addClass('tabsRightDisabled')
                }
            },
            switchTab: function (iTabIndex) {
                var $tab = this.getTabs().removeClass('active').eq(iTabIndex).addClass('active'), $panels = this.getPanels(), $panel = $panels.eq(iTabIndex), onSwitch = that.options.onSwitch ? that.options.onSwitch.toFunc() : null
                var $ajaxBackground = $(FRAG.maskBackground)

                $panels.css({ top: '-10000000px' })

                if ($tab.data('reloadFlag')) {
                    $panel.animate({ top: 0 })
                    that.refresh($tab.data('options').id)
                } else {
                    if ($panel.find('.bjui-ajax-mask').length) {
                        $panel.css({ top: 0 })
                    } else {
                        $panel
                            .css({ top: 0 })
                            .append($ajaxBackground)

                        $ajaxBackground.fadeOut('normal', function () {
                            $(this).remove()
                        })
                    }
                }

                this.getMoreLi().removeClass('active').eq(iTabIndex).addClass('active')
                currentIndex = iTabIndex
                this.scrollCurrent()
                $currentTab = $tab
                $.CurrentNavtab = $currentPanel = $panel

                if (onSwitch) onSwitch.apply(that)

                // set current to body data
                var datas = $('body').data('bjui.navtab'), id = $tab.data('options').id

                if (id !== that.options.id)
                    datas.current = id
                else {
                    datas.current = that.options.id
                }

                // events
                $panel.trigger('switch.bjui.navtab')
            },
            closeTab: function (index, openTabid) {
                var $tab = this.getTabs().eq(index),
                    $more = this.getMoreLi().eq(index),
                    $panel = this.getPanels().eq(index),
                    options = $tab.data('options'),
                    beforeClose = options.beforeClose ? options.beforeClose.toFunc() : null,
                    onClose = options.onClose ? options.onClose.toFunc() : null,
                    canClose = true

                if (beforeClose) canClose = beforeClose.apply(that, [$panel])
                if (!canClose) {
                    that.tools.switchTab(index)
                    return
                }
                $tab.remove()
                $more.remove()
                $panel.trigger(BJUI.eventType.beforeCloseNavtab).remove()

                if (autorefreshTimer) clearInterval(autorefreshTimer)
                if (onClose) onClose.apply(that)
                if (currentIndex >= index) currentIndex--
                if (openTabid) {
                    var openIndex = this.indexTabId(openTabid)

                    if (openIndex > 0) currentIndex = openIndex
                }

                // remove from body
                var datas = $('body').data('bjui.navtab')

                if (datas[options.id])
                    delete datas[options.id]

                this.scrollCurrent()
                this.switchTab(currentIndex)
            },
            closeOtherTab: function (index) {
                index = index || currentIndex

                this.getTabs().each(function (i) {
                    if (i > 0 && index != i) $(this).find('> .close').trigger('click')
                })
            },
            loadUrlCallback: function ($panel) {
                $panel.find(':button.btn-close').click(function () { that.closeCurrentTab() })
            },
            updateTit: function (index, title) {
                this.getTabs().eq(index).find('> a').attr('title', title).find('> span').html(title)
                this.getMoreLi().eq(index).find('> a').attr('title', title).html(title)
            },
            reload: function ($tab, flag) {
                flag = flag || $tab.data('reloadFlag')

                var options = $tab.data('options')

                if (flag) {
                    $tab.data('reloadFlag', false)
                    var $panel = that.tools.getPanel(options.id)

                    if ($tab.hasClass('external')) {
                        that.openExternal(options.url, $panel, options.data)
                    } else {
                        that.tools.reloadTab($panel, options)
                    }
                }
            },
            reloadTab: function ($panel, options) {
                var onLoad = options.onLoad ? options.onLoad.toFunc() : null,
                    arefre = options.autorefresh && (isNaN(String(options.autorefresh)) ? 15 : options.autorefresh)

                $panel
                    .trigger(BJUI.eventType.beforeLoadNavtab)
                    .ajaxUrl({
                        type: (options.type || 'GET'), url: options.url, data: options.data || {}, loadingmask: options.loadingmask, callback: function (response) {
                            that.tools.loadUrlCallback($panel)
                            if (onLoad) onLoad.apply(that, [$panel])
                            if (autorefreshTimer) clearInterval(autorefreshTimer)
                            if (arefre) autorefreshTimer = setInterval(function () { $panel.navtab('refresh') }, arefre * 1000)
                            if (BJUI.ui.clientPaging && $panel.data('bjui.clientPaging')) $panel.pagination('setPagingAndOrderby', $panel)
                        }
                    })
            }
        }

        return tools
    }

    Navtab.prototype.contextmenu = function ($obj) {
        var that = this

        $obj.contextmenu({
            id: 'navtabCM',
            bindings: {
                reload: function (t, m) {
                    if (t.data('options').url)
                        that.refresh(t.data('options').id)
                },
                closeCurrent: function (t, m) {
                    var tabId = t.data('options').id

                    if (tabId) that.closeTab(tabId)
                    else that.closeCurrentTab()
                },
                closeOther: function (t, m) {
                    if (!t.index()) {
                        that.closeAllTab()
                    } else {
                        var index = that.tools.indexTabId(t.data('options').id)

                        that.tools.closeOtherTab(index > 0 ? index : currentIndex)
                    }
                },
                closeAll: function (t, m) {
                    that.closeAllTab()
                }
            },
            ctrSub: function (t, m) {
                var mReload = m.find('[rel="reload"]'),
                    mCur = m.find('[rel="closeCurrent"]'),
                    mOther = m.find('[rel="closeOther"]'),
                    mAll = m.find('[rel="closeAll"]'),
                    $tabLi = that.tools.getTabs()

                if (!t.index()) {
                    mCur.addClass('disabled')
                    if (!t.data('options').url) mReload.addClass('disabled')
                }
            }
        })
    }

    // if found tabid replace tab, else create a new tab.
    Navtab.prototype.openTab = function () {
        var that = this, options = this.options, tools = this.tools, iOpenIndex

        if (!options.url && options.href) options.url = options.href

        if (!options.url) {
            BJUI.debug('BJUI.Navtab: Error trying to open a navtab, url is undefined!')
            return
        } else {
            options.url = decodeURI(options.url).replacePlh()

            if (!options.url.isFinishedTm()) {
                $('body').alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('BJUI.Navtab: The new navtab\'s url is incorrect, url: ' + options.url)
                return
            }

            options.url = encodeURI(options.url)
        }

        iOpenIndex = options.id ? tools.indexTabId(options.id) : currentIndex

        if (iOpenIndex >= 0) {
            var $tab = tools.getTabs().eq(iOpenIndex),
                $panel = tools.getPanels().eq(iOpenIndex),
                op = $tab.data('options')

            if (options.fresh || options.url != op.url)
                that.reload(options)
            else if (options.title != op.title) {
                op.title = options.title
                tools.updateTit(iOpenIndex, options.title)
            }

            currentIndex = iOpenIndex

            if (options.id == 'main') {
                this.contextmenu($tab)
            }
        } else {
            var tabFrag = '<li><a href="javascript:" title="#title#"><span>#title#</span></a><span class="close">&times;</span></li>',
                $tab = $(tabFrag.replaceAll('#title#', options.title)),
                $panel = $('<div class="navtabPage unitBox"></div>'),
                $more = $('<li><a href="javascript:" title="#title#">#title#</a></li>'.replaceAll('#title#', options.title))

            $tab.appendTo($tabs)
            $panel.appendTo($panels)
            $more.appendTo($moreBox)

            $tab.data('options', $.extend({}, options))

            if (options.external || (options.url && options.url.isExternalUrl())) {
                $tab.addClass('external')
                this.openExternal(options.url, $panel, options.data)
            } else {
                $tab.removeClass('external')
                tools.reloadTab($panel, options)
            }

            currentIndex = tools.getTabs().length - 1
            this.contextmenu($tab)

            //events
            $tab.on('click', function (e) {
                if (!$(this).hasClass('active'))
                    that.switchTab(options.id)
            }).on('click.bjui.navtab.close', '.close', function (e) {
                that.closeTab(options.id)
            }).on('mousedown.bjui.navtab.drag', 'a', function (e) {
                $tab.data('bjui.navtab.drag', true)

                setTimeout($.proxy(function () {
                    if ($tab.data('bjui.navtab.drag')) that.drag(e, $tab, $panel, $more)
                }, that), 150)

                e.preventDefault()
            }).on('mouseup.bjui.navtab.drag', 'a', function (e) {
                $tab.data('bjui.navtab.drag', false)
            })

            $more.on('click', function () {
                that.switchTab(options.id)
            })
        }

        tools.switchTab(currentIndex)
        tools.scrollCurrent()
    }

    Navtab.prototype.closeTab = function (tabid) {
        var index = this.tools.indexTabId(tabid)

        if (index > 0)
            this.tools.closeTab(index)
    }

    Navtab.prototype.closeCurrentTab = function (openTabid) { //openTabid can be empty. close current tab by default, and open the last tab
        if (currentIndex > 0)
            this.tools.closeTab(currentIndex, openTabid)
    }

    Navtab.prototype.closeAllTab = function () {
        this.tools.getTabs().find('> .close').trigger('click')
    }

    Navtab.prototype.reloadFlag = function (tabids) {
        var arr = tabids.split(',')

        for (var i = 0; i < arr.length; i++) {
            var $tab = this.tools.getTab(arr[i].trim())

            if ($tab) {
                if (this.tools.indexTabId(arr[i]) == currentIndex) this.tools.reload($tab, true)
                else $tab.data('reloadFlag', true)
            }
        }
    }

    Navtab.prototype.switchTab = function (tabid) {
        var index = this.tools.indexTabId(tabid)

        this.tools.switchTab(index)
    }

    Navtab.prototype.scrollPrev = function () {
        this.tools.scrollPrev()
    }

    Navtab.prototype.scrollNext = function () {
        this.tools.scrollNext()
    }

    Navtab.prototype.refresh = function (tabid) {
        var $tab, $panel

        if (!tabid) {
            $tab = $currentTab
        } else if (typeof tabid === 'string') {
            $tab = this.tools.getTab(tabid)
        } else {
            $tab = tabid
        }

        if ($tab && $tab.length) {
            $panel = this.tools.getPanel($tab.data('options').id)
            $panel.removeData('bjui.clientPaging')

            this.reload($tab.data('options'))
        }
    }

    Navtab.prototype.reload = function (option) {
        var that = this,
            options = $.extend({}, typeof option === 'object' && option),
            $tab = options.id ? this.tools.getTab(options.id) : this.tools.getTabs().eq(currentIndex)

        if ($tab) {
            var op = $tab.data('options')
            var _reload = function () {
                if (options.title && options.title != op.title) {
                    that.tools.updateTit($tab.index(), options.title)
                }
                $tab.data('options', $.extend({}, op, options))
                that.tools.reload($tab, true)
            }

            if (op.reloadWarn) {
                $('body').alertmsg('confirm', op.reloadWarn, {
                    okCall: function () {
                        _reload()
                    }
                })
            } else {
                _reload()
            }
        }
    }

    Navtab.prototype.reloadForm = function (clearQuery, option) {
        var options = $.extend({}, typeof option === 'object' && option),
            $tab = options.id ? this.tools.getTab(options.id) : $currentTab,
            $panel = options.id ? this.tools.getPanel(options.id) : $currentPanel

        if ($tab && $panel) {
            var op = $tab.data('options'),
                data = {},
                pageData = {},
                $pagerForm = options.form

            if ($pagerForm && $pagerForm.length) {
                options.type = options.type || $pagerForm.attr('method') || 'POST'
                options.url = options.url || $pagerForm.attr('action')

                pageData = $pagerForm.serializeJson()

                if (clearQuery) {
                    var pageInfo = BJUI.pageInfo

                    for (var key in pageInfo) {
                        data[pageInfo[key]] = pageData[pageInfo[key]]
                    }
                } else {
                    data = pageData
                }
            }

            options.data = $.extend({}, options.data || {}, data)

            if (!$tab.hasClass('external')) {
                this.tools.reloadTab($panel, options)
            } else {
                this.openExternal(options.url, $panel, options.data)
            }
        }
    }

    Navtab.prototype.getCurrentPanel = function () {
        return this.tools.getPanels().eq(currentIndex)
    }

    Navtab.prototype.checkTimeout = function () {
        var json = JSON.parse($currentPanel.html())

        if (json && json[BJUI.keys.statusCode] == BJUI.statusCode.timeout) this.closeCurrentTab()
    }

    Navtab.prototype.openExternal = function (url, $panel, data) {
        var ih = $panel.closest('.navtab-panel').height()

        if (data && !$.isEmptyObject(data)) {
            url.indexOf('?') ? url += '&' : '?'
            url += $.param(data)
        }

        $panel.html(FRAG.externalFrag.replaceAll('{url}', url).replaceAll('{height}', ih + 'px'))
    }

    Navtab.prototype.drag = function (e, $tab, $panel, $more) {
        var that = this,
            $lis = that.tools.getTabs(), $panels = that.tools.getPanels(), $mores = that.tools.getMoreLi(),
            $drag = $tabs.next('.bjui-navtab-drag'),
            $prev = $tab.prev(), $next = $tab.next(),
            index = $tab.index(), width = $tab.width(),
            oleft = $tab.position().left,
            leftarr = [], newArr = [], newIndex

        if ($lis.length <= 2) return

        if (!$drag.length) {
            $drag = $('<div class="bjui-navtab-drag" style="position:absolute; top:0; left:1px; width:2px; height:25px; background:#ff6600; display:none;"></div>')
            $drag.insertAfter($tabs)
        }

        $drag.css('left', oleft - 2)

        $tabs.find('> li').each(function () {
            leftarr.push($(this).position().left)
        })

        $tab.find('> a').basedrag({
            move: 'horizontal',
            oleft: oleft,
            drag: function ($target, e, left, top) {
                $tab.addClass('navtab-drag')

                newArr = [left]
                $.merge(newArr, leftarr)
                newArr.sort(function (a, b) { return a - b })

                newIndex = $.inArray(left, newArr)
                if (!newIndex) newIndex = 1
                if (newIndex == $lis.length)
                    $drag.css('left', leftarr[newIndex - 1] - 2 + $lis.last().width())
                else
                    $drag.css('left', leftarr[newIndex] - 2)

                $drag.show()
            },
            stop: function () {
                $tab.removeClass('navtab-drag')
                $drag.hide()

                if (index != newIndex) {
                    if (newIndex == $lis.length) {
                        if (index != $lis.length - 1) {
                            $tab.insertAfter($lis.eq(newIndex - 1))
                            $panel.insertAfter($panels.eq(newIndex - 1))
                            $more.insertAfter($mores.eq(newIndex - 1))
                        }
                    } else {
                        $tab.insertBefore($lis.eq(newIndex))
                        $panel.insertBefore($panels.eq(newIndex))
                        $more.insertBefore($mores.eq(newIndex))
                    }
                }
            },
            event: e,
            nounbind: true
        })
    }

    // NAVTAB PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments,
            property = option,
            navtab = 'bjui.navtab',
            $body = $('body'),
            datas = $body.data(navtab) || {}

        return this.each(function () {
            var $this = $(this),
                options = $.extend({}, Navtab.DEFAULTS, typeof option === 'object' && option),
                id = options && options.id,
                data = datas && datas[datas.current]

            if (typeof property === 'string' && $.isFunction(data[property])) {
                if (!data) return

                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                if (!id) {
                    id = datas ? datas.current : 'main'

                    if (!BJUI.ui.overwriteHomeTab && id === 'main')
                        id = 'navtab'
                } else {
                    if (!id.isNormalID()) {
                        BJUI.debug('BJUI.Navtab: ID [' + id + '] ' + BJUI.regional.idChecked)

                        return
                    }
                }

                options.id = id

                if (!datas[id]) {
                    datas[id] = (data = new Navtab(options))
                } else {
                    data = datas[id]
                    if (typeof option === 'object' && option)
                        $.extend(data.options, option)
                }

                $body.data(navtab, datas)

                data.openTab()
            }
        })
    }

    var old = $.fn.navtab

    $.fn.navtab = Plugin
    $.fn.navtab.Constructor = Navtab

    // NAVTAB NO CONFLICT
    // =================

    $.fn.navtab.noConflict = function () {
        $.fn.navtab = old
        return this
    }

    // NOT SELECTOR
    // ==============

    BJUI.navtab = function () {
        Plugin.apply($('body'), arguments)
    }

    // NAVTAB DATA-API
    // ==============

    $(document).on('click.bjui.navtab.data-api', '[data-toggle="navtab"]', function (e) {
        e.preventDefault()

        var $this = $(this), href = $this.attr('href'), data = $this.data(), options = data.options

        if (options) {
            if (typeof options === 'string') options = options.toObj()
            if (typeof options === 'object')
                $.extend(data, options)
        }

        if (!data.title) data.title = $this.text()
        if (href && !data.url) data.url = href

        Plugin.call($this, data)
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-dialog.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-dialog.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // DIALOG GLOBAL ELEMENTS
    // ======================

    var $resizable
    var $current, shadow, zindex

    $(function () {
        var INIT_DIALOG = function () {
            $resizable = $('#bjui-resizable')
            shadow = 'dialogShadow'
            zindex = Dialog.ZINDEX

            $('body').append('<!-- dialog resizable -->').append(FRAG.resizable)
        }

        INIT_DIALOG()
    })

    // DIALOG CLASS DEFINITION
    // ======================
    var Dialog = function (options) {
        this.$element = $('body')
        this.options = options
        this.tools = this.TOOLS()
    }

    Dialog.DEFAULTS = {
        id: null,
        title: 'New Dialog',
        url: null,
        type: 'GET',
        data: {},
        loadingmask: true,
        width: 500,
        height: 300,
        minW: 65,
        minH: 40,
        max: false,
        mask: false,
        resizable: true,
        drawable: true,
        maxable: true,
        minable: true,
        fresh: false,
        onLoad: null,
        beforeClose: null,
        onClose: null
    }

    Dialog.ZINDEX = 30

    Dialog.prototype.TOOLS = function () {
        var that = this
        var tools = {
            getDefaults: function () {
                return Dialog.DEFAULTS
            },
            init: function ($dialog) {
                var width = that.options.width > that.options.minW ? that.options.width : that.options.minW
                var height = that.options.height > that.options.minH ? that.options.height : that.options.minH
                var wW = $(window).width(),
                    wH = $(window).height(),
                    iTop = that.options.max ? 0 : ((wH - height) / 3)

                if (width > wW) width = wW
                if (height > wH) height = wH

                $dialog
                    .height(height)
                    .width(width)
                    .show()
                    .css({ left: (wW - width) / 2, top: 0, opacity: 0.1 })
                    .animate({ top: iTop > 0 ? iTop : 0, opacity: 1 })
                    .addClass(shadow)
                    .find('> .dialogContent').height(height - $('> .dialogHeader', $dialog).outerHeight())

                $('body').find('> .bjui-dialog-container').not($dialog).removeClass(shadow)
            },
            reload: function ($dialog, options) {
                var tools = this, $dialogContent = $dialog.find('> .dialogContent'), onLoad, data = options && options.data, html

                options = options || $dialog.data('options')
                onLoad = options.onLoad ? options.onLoad.toFunc() : null

                $dialog.trigger(BJUI.eventType.beforeLoadDialog)

                if (options.url) {
                    if (data) {
                        if (typeof data === 'string') {
                            if (data.trim().startsWith('{')) {
                                data = data.toObj()
                            } else {
                                data = data.toFunc()
                            }
                        }
                        if (typeof data === 'function') {
                            data = data.apply()
                        }
                    }
                    $dialogContent.ajaxUrl({
                        type: options.type || 'GET', url: options.url, data: data || {}, loadingmask: options.loadingmask, callback: function (response) {
                            if (onLoad) onLoad.apply(that, [$dialog])
                            if (BJUI.ui.clientPaging && $dialog.data('bjui.clientPaging')) $dialog.pagination('setPagingAndOrderby', $dialog)

                            tools.resizeBjuiRow($dialog)
                        }
                    })
                } else {
                    if (options.image) {
                        html = '<img src="' + decodeURIComponent(options.image) + '" style="max-width:100%;">'
                    } else if (options.html) {
                        html = options.html
                        if (typeof html === 'string' && $.isFunction(html.toFunc()))
                            html = html.toFunc()
                    } else if (options.target) {
                        html = $(options.target).html() || $dialog.data('bjui.dialog.target')
                        $(options.target).empty()
                        $dialog.data('bjui.dialog.target', html)
                    }

                    $dialogContent.trigger(BJUI.eventType.beforeAjaxLoad).html(html).initui()

                    if (onLoad) onLoad.apply(that, [$dialog])

                    this.resizeBjuiRow($dialog)
                }
            },
            resizeContent: function ($dialog) {
                var $dialogContent = $dialog.find('> .dialogContent')

                $dialogContent
                    .css({ height: ($dialog.height() - $dialog.find('> .dialogHeader').outerHeight()) })

                $(window).trigger(BJUI.eventType.resizeGrid)
            },
            resizeBjuiRow: function ($dialog) {
                var width = $dialog.width(), colWidth = BJUI.formColWidth

                $dialog.find('.bjui-row').each(function () {
                    var $rows = $(this)

                    if (($rows.attr('class')).indexOf('col-') === -1) {
                        $rows.addClass('col-none')
                    }

                    $rows.filter('.col-4')
                        .toggleClass('col-3', (width < colWidth.L && width > colWidth.M))
                        .end()
                        .filter('.col-3, .col-4')
                        .toggleClass('col-2', (width < colWidth.M && width > colWidth.S))
                        .end()
                        .filter('.col-2, .col-3, .col-4')
                        .toggleClass('col-1', (width < colWidth.S && width > colWidth.SS))
                        .end()
                        .filter('.col-none, .col-1, .col-2, .col-3, .col-4')
                        .toggleClass('col-0', (width < colWidth.SS))
                })
            }
        }

        return tools
    }

    Dialog.prototype.open = function () {
        var that = this,
            options = that.options,
            $body = $('body'),
            datas = $body.data('bjui.dialog'),
            data = datas[options.id],
            $dialog = data && data.dialog

        if (!(options.target || $(options.target).length) && !(options.html || options.image)) {
            if (!options.url && options.href) options.url = options.href
            if (!options.url) {
                BJUI.debug('BJUI.Dialog: Error trying to open a dialog, url is undefined!')
                return
            } else {
                options.url = decodeURI(options.url).replacePlh()

                if (!options.url.isFinishedTm()) {
                    BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                    BJUI.debug('BJUI.Dialog: The new dialog\'s url is incorrect, url: ' + options.url)
                    return
                }

                options.url = encodeURI(options.url)
            }
        } else {
            options.url = undefined
        }
        if ($dialog) { //if the dialog id already exists
            var op = $dialog.data('options')

            this.switchDialog($dialog)

            if ($dialog.is(':hidden')) $dialog.show()
            if (options.fresh || options.url != op.url) {
                that.reload(options)
            }
        } else { //open a new dialog
            var dr = BJUI.regional.dialog,
                dialog = FRAG.dialog
                    .replace('#close#', dr.close)
                    .replace('#maximize#', dr.maximize)
                    .replace('#restore#', dr.restore)
                    .replace('#minimize#', dr.minimize)
                    .replace('#title#', dr.title)

            $dialog = $(dialog)
                .data('options', $.extend({}, options))
                .css('z-index', (++zindex))
                .hide()
                .appendTo($body)

            $dialog.find('> .dialogHeader > h1 > span.title').html(options.title)

            this.tools.init($dialog)

            if (options.maxable) $dialog.find('a.maximize').show()
            else $dialog.find('a.maximize').hide()
            if (options.minable) $dialog.find('a.minimize').show()
            else $dialog.find('a.minimize').hide()
            if (options.max) that.maxsize($dialog)
            if (options.mask) this.addMask($dialog)
            else if (options.minable && $.fn.taskbar) this.$element.taskbar({ id: options.id, title: options.title })

            $dialog.on('click', function (e) {
                if (!$(e.target).data('bjui.dialog'))
                    if ($current && $current[0] != $dialog[0]) that.switchDialog($dialog)
            }).on('click', '.btn-close', function (e) {
                that.close($dialog)

                e.preventDefault()
            }).on('click', '.dialogHeader > a', function (e) {
                var $a = $(this)

                if ($a.hasClass('close')) that.close($dialog)
                if ($a.hasClass('minimize')) {
                    that.minimize($dialog)
                }
                if ($a.hasClass('maximize')) {
                    that.switchDialog($dialog)
                    that.maxsize($dialog)
                }
                if ($a.hasClass('restore')) that.restore($dialog)

                e.preventDefault()
                e.stopPropagation()
            }).on('dblclick', '.dialogHeader > h1', function (e) {
                if (options.maxable) {
                    if ($dialog.find('> .dialogHeader > a.restore').is(':hidden')) $dialog.find('a.maximize').trigger('click')
                    else $dialog.find('> .dialogHeader > a.restore').trigger('click')
                }
            }).on('mousedown.bjui.dialog.drag', '.dialogHeader > h1', function (e) {
                that.switchDialog($dialog)

                if (!options.drawable || $dialog.data('max')) return

                $dialog.data('bjui.dialog.task', true)
                setTimeout($.proxy(function () {
                    if ($dialog.data('bjui.dialog.task')) that.drag(e, $dialog)
                }, that), 150)

                e.preventDefault()
            }).on('mouseup.bjui.dialog.drag', '.dialogHeader > h1', function (e) {
                $dialog.data('bjui.dialog.task', false)
            }).on('mousedown.bjui.dialog.resize', 'div[class^="resizable"]', function (e) {
                if (!options.drawable || $dialog.data('max')) return false
                if (!options.resizable) return false

                var $bar = $(this)

                that.switchDialog($dialog)
                that.resizeInit(e, $('#bjui-resizable'), $dialog, $bar)
                $bar.show()

                e.preventDefault()
            }).on('mouseup.bjui.dialog.resize', 'div[class^="resizable"]', function (e) {
                e.preventDefault()
            })

            data.dialog = $dialog
            this.tools.reload($dialog, options)
        }

        $.CurrentDialog = $current = $dialog

        // set current to body data
        datas.current = options.id
    }

    Dialog.prototype.addMask = function ($dialog) {
        var $mask = $dialog.data('bjui.dialog.mask')

        $dialog.wrap('<div style="z-index:' + zindex + '" class="bjui-dialog-wrap"></div>')
        $dialog.find('> .dialogHeader > a.minimize').hide()
        if (!$mask || !$mask.length) {
            $mask = $(FRAG.dialogMask)
            $mask.css('z-index', 1).show().insertBefore($dialog)
            $dialog.data('bjui.dialog.mask', $mask)
        }
    }

    Dialog.prototype.refresh = function (id) {
        if (id && typeof id === 'string') {
            var arr = id.split(','), datas = $('body').data('bjui.dialog')

            for (var i = 0; i < arr.length; i++) {
                var $dialog = datas && datas[arr[i].trim()] && datas[arr[i].trim()].dialog

                if ($dialog) {
                    $dialog.removeData('bjui.clientPaging')
                    this.tools.reload($dialog)
                }
            }
        } else {
            if ($current) {
                $current.removeData('bjui.clientPaging')
                this.tools.reload($current)
            }
        }
    }

    Dialog.prototype.resize = function ($dialog, width, height) {
        var that = this, options, $dialogContent, ww = $(window).width(), hh = $(window).height()

        if (!$dialog) {
            $dialog = $current
        }

        options = $dialog.data('options')
        $dialogContent = $dialog.find('> .dialogContent')

        if (width != options.width) {
            if (width > ww)
                width = ww

            if (options.max) {
                $dialog.animate({ width: width, left: (ww - width) / 2 }, 'normal')
            } else {
                $dialog.width(width).css('left', (ww - width) / 2)
            }

            options.width = width
        }
        if (height != options.height) {
            var itop = 0

            if (height < hh)
                itop = (hh - height) / 2 - 20
            if (itop < 0)
                itop = 0

            if (options.max) {
                $dialog.animate({ height: height, top: itop }, 'normal', function () {
                    $dialogContent.height(height - $dialog.find('> .dialogHeader').outerHeight())
                })
            } else {
                $dialog.height(height).css('top', itop)
                $dialogContent.height(height - $dialog.find('> .dialogHeader').outerHeight())
            }

            options.height = height
        }
    }

    Dialog.prototype.reload = function (option) {
        var that = this,
            options = $.extend({}, typeof option === 'object' && option),
            datas = $('body').data('bjui.dialog'),
            $dialog = (options.id && datas[options.id] && datas[options.id].dialog) || that.getCurrent()

        if ($dialog && $dialog.length) {
            var op = $dialog.data('options')

            options = $.extend({}, op, options)

            var _reload = function () {
                var $dialogContent = $dialog.find('> .dialogContent'), ww = $(window).width(), hh = $(window).height(), w = options.width, h = options.height

                if (w != op.width) {
                    if (w > ww)
                        w = ww

                    if (options.max) {
                        $dialog.animate({ width: w, left: (ww - w) / 2 }, 'normal'/*, function() { $dialogContent.width(options.width) }*/)
                    } else {
                        $dialog.width(w).css('left', (ww - w) / 2)
                    }
                }
                if (h != op.height) {
                    var itop = 0

                    if (h < hh)
                        itop = (hh - h) / 2 - 20
                    if (itop < 0)
                        itop = 0

                    if (options.max) {
                        $dialog.animate({ height: h, top: itop }, 'normal', function () {
                            $dialogContent.height(h - $dialog.find('> .dialogHeader').outerHeight())
                        })
                    } else {
                        $dialog.height(h).css('top', itop)
                        $dialogContent.height(h - $dialog.find('> .dialogHeader').outerHeight())
                    }
                }
                if (options.maxable != op.maxable) {
                    if (options.maxable) $dialog.find('a.maximize').show()
                    else $dialog.find('a.maximize').hide()
                }
                if (options.minable != op.minable) {
                    if (options.minable) $dialog.find('a.minimize').show()
                    else $dialog.find('a.minimize').hide()
                }
                if (options.max != op.max)
                    if (options.max)
                        setTimeout(that.maxsize($dialog), 10)
                if (options.mask != op.mask) {
                    if (options.mask) {
                        that.addMask($dialog)
                        if ($.fn.taskbar) that.$element.taskbar('closeDialog', options.id)
                    } else if (options.minable && $.fn.taskbar) {
                        that.$element.taskbar({ id: options.id, title: options.title })
                    }
                }
                if (options.title != op.title) {
                    $dialog.find('> .dialogHeader > h1 > span.title').html(options.title)
                    $dialog.taskbar('changeTitle', options.id, options.title)
                }

                $dialog.data('options', $.extend({}, options))

                that.tools.reload($dialog, options)
            }

            if (options.reloadWarn) {
                $dialog.alertmsg('confirm', options.reloadWarn, {
                    okCall: function () {
                        _reload()
                    }
                })
            } else {
                _reload()
            }
        }
    }

    Dialog.prototype.reloadForm = function (clearQuery, option) {
        var options = $.extend({}, typeof option === 'object' && option),
            datas = $('body').data('bjui.dialog'),
            $dialog

        if (options.id) {
            if (datas && datas[options.id])
                $dialog = datas[options.id].dialog
        } else {
            $dialog = $current
        }

        if ($dialog) {
            var op = $dialog.data('options'),
                data = {},
                pageData = {},
                $pagerForm = options.form

            if ($pagerForm && $pagerForm.length) {
                options.type = options.type || $pagerForm.attr('method') || 'POST'
                options.url = options.url || $pagerForm.attr('action')

                pageData = $pagerForm.serializeJson()

                if (clearQuery) {
                    var pageInfo = BJUI.pageInfo

                    for (var key in pageInfo) {
                        data[pageInfo[key]] = pageData[pageInfo[key]]
                    }
                } else {
                    data = pageData
                }
            }

            options.data = $.extend({}, options.data || {}, data)

            this.tools.reload($dialog, options)
        }
    }

    Dialog.prototype.getCurrent = function () {
        return $current
    }

    Dialog.prototype.switchDialog = function ($dialog) {
        var index = $dialog.css('z-index')

        if ($current && $current != $dialog) {
            var cindex = $current.css('z-index'),
                datas = $('body').data('bjui.dialog'),
                pindex

            if ($current.data('options').mask) {
                pindex = $current.parent().css('z-index')

                if (Number(pindex) > Number(index))
                    return
            }

            $current.css('z-index', index)
            $dialog.css('z-index', cindex)
            $.CurrentDialog = $current = $dialog

            // set current to body data
            datas.current = $dialog.data('options').id

            if ($.fn.taskbar) this.$element.taskbar('switchTask', datas.current)
        }

        $dialog.addClass(shadow)
        $('body').find('> .bjui-dialog-container, > .bjui-dialog-wrap > .bjui-dialog-container').not($dialog).removeClass(shadow)
    }

    Dialog.prototype.close = function (dialog) {
        var datas = $('body').data('bjui.dialog'), $dialog = (typeof dialog === 'string') ? datas[dialog].dialog : dialog

        if (!$dialog || !$dialog.length) {
            return
        }

        var that = this,
            $mask = $dialog.data('bjui.dialog.mask'),
            options = $dialog.data('options'),
            target = $dialog.data('bjui.dialog.target'),
            beforeClose = options.beforeClose ? options.beforeClose.toFunc() : null,
            onClose = options.onClose ? options.onClose.toFunc() : null,
            canClose = true,
            closeFunc = function () {
                delete datas[options.id]

                if (onClose) onClose.apply(that)

                $.CurrentDialog = $current = null

                var $dialogs = $('body').find('.bjui-dialog-container'),
                    $_current = null

                if ($dialogs.length) {
                    $_current = that.$element.getMaxIndexObj($dialogs)
                } else {
                    zindex = Dialog.ZINDEX
                }
                if ($_current && $_current.is(':visible')) {
                    $.CurrentDialog = $current = $_current
                    that.switchDialog($_current)
                }
            }

        if (!$dialog || !options) return
        if (beforeClose) canClose = beforeClose.apply(that, [$dialog])
        if (!canClose) {
            that.switchDialog($dialog)
            return
        }
        if (options.target && target) $(options.target).html(target)
        if ($mask && $mask.length) {
            $mask.remove()
            $dialog.unwrap()
        } else if ($.fn.taskbar) {
            this.$element.taskbar('closeDialog', options.id)
        }

        if (options.noanimate) {
            $dialog.trigger(BJUI.eventType.beforeCloseDialog).remove()
            closeFunc()
        } else {
            $dialog.animate({ top: - $dialog.outerHeight(), opacity: 0.1 }, 'normal', function () {
                $dialog.trigger(BJUI.eventType.beforeCloseDialog).remove()
                closeFunc()
            })
        }
    }

    Dialog.prototype.closeCurrent = function () {
        this.close($current)
    }

    Dialog.prototype.checkTimeout = function () {
        var $dialogConetnt = $current.find('> .dialogContent'),
            json = JSON.parse($dialogConetnt.html())

        if (json && json[BJUI.keys.statusCode] == BJUI.statusCode.timeout) this.closeCurrent()
    }

    Dialog.prototype.maxsize = function ($dialog) {
        var $taskbar = $('#bjui-taskbar'), taskH = ($taskbar.is(':visible') ? $taskbar.height() : 0) + 1

        $dialog.data('original', {
            top: $dialog.css('top'),
            left: $dialog.css('left'),
            width: $dialog.css('width'),
            height: $dialog.css('height')
        }).data('max', true)

        $dialog.find('> .dialogHeader > a.maximize').hide()
        $dialog.find('> .dialogHeader > a.restore').show()

        var iContentW = $(window).width() - 1,
            iContentH = $(window).height() - taskH

        $dialog.css({ top: 0, right: 0, left: 0, bottom: taskH, width: '100%', height: 'auto' })

        this.tools.resizeContent($dialog)
        this.tools.resizeBjuiRow($dialog)
    }

    Dialog.prototype.restore = function ($dialog) {
        var original = $dialog.data('original'),
            dwidth = original.width,
            dheight = original.height

        $dialog.css({
            top: original.top,
            right: '',
            bottom: '',
            left: original.left,
            width: dwidth,
            height: dheight
        })

        this.tools.resizeContent($dialog)
        this.tools.resizeBjuiRow($dialog)

        $dialog.find('> .dialogHeader > a.maximize').show()
        $dialog.find('> .dialogHeader > a.restore').hide()
        $dialog.data('max', false)
    }

    Dialog.prototype.minimize = function ($dialog) {
        $dialog.hide()
        if ($.fn.taskbar) this.$element.taskbar('minimize', $dialog)

        var $dialogs = $('body').find('.bjui-dialog-container:visible'),
            $_current = null

        if ($dialogs.length) {
            $_current = this.$element.getMaxIndexObj($dialogs)
        }
        if ($_current) this.switchDialog($_current)
    }

    Dialog.prototype.drag = function (e, $dialog) {
        var $shadow = $('#bjui-dialogProxy')

        $dialog.find('> .dialogContent').css('opacity', '.3')
        $dialog.basedrag({
            selector: '> .dialogHeader',
            stop: function () {
                $dialog
                    .css({ left: $dialog.css('left'), top: $dialog.css('top') })
                    .find('> .dialogContent').css('opacity', 1)
            },
            event: e,
            nounbind: true
        })
    }

    Dialog.prototype.resizeDialog = function ($resizable, $dialog, target) {
        var oleft = parseInt($resizable.css('left'), 10),
            otop = parseInt($resizable.css('top'), 10),
            height = parseInt($resizable.css('height'), 10),
            width = parseInt($resizable.css('width'), 10)

        if (otop < 0) otop = 0

        $dialog.css({ top: otop, left: oleft, width: width, height: height })

        if (target != 'w' && target != 'e')
            this.tools.resizeContent($dialog)
        if (target != 'n' && target != 's') {
            this.tools.resizeBjuiRow($dialog)
            $(window).trigger(BJUI.eventType.resizeGrid)
        }
    }

    Dialog.prototype.resizeInit = function (e, $resizable, $dialog, $bar) {
        var that = this, target = $bar.attr('tar')

        $('body').css('cursor', target + '-resize')
        $resizable
            .css({
                top: $dialog.css('top'),
                left: $dialog.css('left'),
                height: $dialog.outerHeight(),
                width: $dialog.css('width')
            })
            .show()

        if (!this.options.dragCurrent) {
            this.options.dragCurrent = {
                $resizable: $resizable,
                $dialog: $dialog,
                target: target,
                oleft: parseInt($resizable.css('left'), 10) || 0,
                owidth: parseInt($resizable.css('width'), 10) || 0,
                otop: parseInt($resizable.css('top'), 10) || 0,
                oheight: parseInt($resizable.css('height'), 10) || 0,
                ox: e.pageX || e.screenX,
                oy: e.pageY || e.clientY
            }
            $(document).on('mouseup.bjui.dialog.resize', $.proxy(that.resizeStop, that))
            $(document).on('mousemove.bjui.dialog.resize', $.proxy(that.resizeStart, that))
        }
    }

    Dialog.prototype.resizeStart = function (e) {
        var current = this.options.dragCurrent

        if (!current) return
        if (!e) var e = window.event

        var lmove = (e.pageX || e.screenX) - current.ox,
            tmove = (e.pageY || e.clientY) - current.oy,
            $mask = current.$dialog.data('bjui.dialog.mask')

        if (!$mask || !$mask.length)
            if ((e.pageY || e.clientY) <= 0 || (e.pageY || e.clientY) >= ($(window).height() - current.$dialog.find('> .dialogHeader').outerHeight())) return

        var target = current.target,
            width = current.owidth,
            height = current.oheight

        if (target != 'n' && target != 's')
            width += (target.indexOf('w') >= 0) ? -lmove : lmove
        if (width >= this.options.minW) {
            if (target.indexOf('w') >= 0)
                current.$resizable.css('left', (current.oleft + lmove))
            if (target != 'n' && target != 's')
                current.$resizable.css('width', width)
        }
        if (target != 'w' && target != 'e')
            height += (target.indexOf('n') >= 0) ? -tmove : tmove
        if (height >= this.options.minH) {
            if (target.indexOf('n') >= 0)
                current.$resizable.css('top', (current.otop + tmove))
            if (target != 'w' && target != 'e')
                current.$resizable.css('height', height)
        }
    }

    Dialog.prototype.resizeStop = function (e) {
        var current = this.options.dragCurrent

        if (!current) return false

        $(document).off('mouseup.bjui.dialog.resize').off('mousemove.bjui.dialog.resize')

        this.options.dragCurrent = null
        this.resizeDialog(current.$resizable, current.$dialog, current.target)

        $('body').css('cursor', '')
        current.$resizable.hide()
    }

    // DIALOG PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments,
            property = option,
            dialog = 'bjui.dialog',
            $body = $('body'),
            datas = $body.data(dialog) || {}

        return this.each(function () {
            var $this = $(this),
                options = $.extend({}, Dialog.DEFAULTS, typeof option === 'object' && option),
                id = options && options.id,
                data

            if (!id) {
                if (datas.current) id = datas.current
                else id = 'dialog'
            } else {
                if (!id.isNormalID()) {
                    BJUI.debug('BJUI.Dialog: ID [' + id + '] ' + BJUI.regional.idChecked)

                    return
                }
            }

            options.id = id
            data = datas && datas[id]

            if (!data) {
                datas[id] = (data = new Dialog(options))
            } else {
                if (typeof option === 'object' && option)
                    $.extend(data.options, option)
            }

            $body.data(dialog, datas)

            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.open()
            }
        })
    }

    var old = $.fn.dialog

    $.fn.dialog = Plugin
    $.fn.dialog.Constructor = Dialog

    // DIALOG NO CONFLICT
    // =================

    $.fn.dialog.noConflict = function () {
        $.fn.dialog = old
        return this
    }

    // NOT SELECTOR
    // ==============

    BJUI.dialog = function () {
        Plugin.apply($('body'), arguments)
    }

    // DIALOG DATA-API
    // ==============

    $(document).on('click.bjui.dialog.data-api', '[data-toggle="dialog"]', function (e) {
        var $this = $(this), href = $this.attr('href'), data = $this.data(), options = data.options

        if (options) {
            if (typeof options === 'string') options = options.toObj()
            if (typeof options === 'object')
                $.extend(data, options)
        }

        if (!data.title) data.title = $this.text()
        if (href && !data.url) data.url = href

        Plugin.call($this, data)

        e.preventDefault()
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-taskbar.js  V1.31
 * reference: bjui-dialog.js
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-taskbar.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // TASKBAR GLOBAL ELEMENTS
    // ======================

    var $resizable
    var $taskBar, $taskBox, $taskList, $prevBtn, $nextBtn, taskDisabled, taskSelected, taskMargin

    $(function () {
        var INIT_TASKBAR = function () {
            $resizable = $('#bjui-resizable')
            $taskBar = $(FRAG.taskbar)
            $taskBox = $taskBar.find('.taskbarContent')
            $taskList = $taskBox.find('> ul')
            $prevBtn = $taskBar.find('.taskbarLeft')
            $nextBtn = $taskBar.find('.taskbarRight')
            taskDisabled = 'disabled'
            taskSelected = 'selected'
            taskMargin = 'taskbarMargin'

            $('body').append('<!-- dialog task bar -->').append($taskBar)

            //events
            $prevBtn.click(function (e) { $(this).taskbar('scrollLeft') })
            $nextBtn.click(function (e) { $(this).taskbar('scrollRight') })
        }
        INIT_TASKBAR()
    })

    // TASKBAR CLASS DEFINITION
    // ======================

    var Taskbar = function (element, options) {
        this.$element = $(element)
        this.$task = null
        this.options = options
        this.tools = this.TOOLS()
    }

    Taskbar.DEFAULTS = {
        id: undefined,
        title: undefined
    }

    Taskbar.prototype.init = function () {
        var that = this
        var $task = $taskList.find('#task-' + this.options.id)

        this.show()
        if (!$task.length) {
            var taskFrag = '<li id="#taskid#"><div class="taskbutton"><span><i class="fa fa-th-large"></i></span> <span class="title">#title#</span></div><div class="close"><i class="fa fa-times-circle"></i></div></li>';

            $task = $(taskFrag.replace('#taskid#', 'task-' + this.options.id).replace('#title#', this.options.title))
            $task.appendTo($taskList)
        } else {
            $task.find('> div > span.title').html(this.options.title)
        }
        this.contextmenu($task)
        this.switchTask($task)
        this.tools.scrollTask($task)

        //events
        $task.click(function (e) {
            if ($(e.target).closest('div').hasClass('close') || $(e.target).hasClass('close')) {
                $task.dialog('close', that.options.id)
            } else {
                var datas = $('body').data('bjui.dialog'),
                    $dialog = datas[that.options.id].dialog

                if ($task.hasClass('selected')) {
                    $dialog.find('.dialogHeader a.minimize').trigger('click')
                } else {
                    if ($dialog.is(':hidden')) {
                        that.restoreDialog($dialog)
                    } else {
                        $dialog.trigger('click')
                        $task.addClass(taskSelected)
                    }
                }
                that.scrollCurrent($task)
            }

            return false
        })
    }

    Taskbar.prototype.TOOLS = function () {
        var that = this
        var tools = {
            scrollCurrent: function () {
                var iW = this.tasksW(this.getTasks())

                if (iW > this.getTaskBarW()) {
                    var $tools = this
                    var lTask = $taskList.find('> li:last-child')
                    var left = this.getTaskBarW() - lTask.position().left - lTask.outerWidth(true)

                    $taskList.animate({ left: left }, 200, function () {
                        $tools.ctrlScrollBtn()
                    })
                } else {
                    this.ctrlScrollBtn()
                }
            },
            getTaskBarW: function () {
                return $taskBox.width() - ($prevBtn.is(':hidden') ? $prevBtn.width() + 2 : 0) - ($nextBtn.is(':hidden') ? $nextBtn.width() + 2 : 0)
            },
            scrollTask: function ($task) {
                var $tools = this

                if ($task.position().left + this.getLeft() + $task.outerWidth() > this.getBarWidth()) {
                    var left = this.getTaskBarW() - $task.position().left - $task.outerWidth(true) - 2

                    $taskList.animate({ left: left }, 200, function () {
                        $tools.ctrlScrollBtn()
                    })
                } else if ($task.position().left + this.getLeft() < 0) {
                    var left = this.getLeft() - ($task.position().left + this.getLeft())

                    $taskList.animate({ left: left }, 200, function () {
                        $tools.ctrlScrollBtn()
                    })
                }
            },
            ctrlScrollBtn: function () {
                var iW = this.tasksW(this.getTasks())

                if (this.getTaskBarW() > iW) {
                    $taskBox.removeClass(taskMargin)
                    $nextBtn.hide()
                    $prevBtn.hide()
                    if (this.getTasks().eq(0).length) this.scrollTask(this.getTasks().eq(0))
                } else {
                    $taskBox.addClass(taskMargin)
                    $nextBtn.show().removeClass(taskDisabled)
                    $prevBtn.show().removeClass(taskDisabled)
                    if (this.getLeft() >= 0) $prevBtn.addClass(taskDisabled)
                    if (this.getLeft() <= this.getTaskBarW() - iW) $nextBtn.addClass(taskDisabled)
                }
            },
            getLeft: function () {
                return $taskList.position().left
            },
            visibleStart: function () {
                var iLeft = this.getLeft()
                var jTasks = this.getTasks()

                for (var i = 0; i < jTasks.size(); i++) {
                    if (jTasks.eq(i).position().left + jTasks.eq(i).outerWidth(true) + iLeft >= 0) return jTasks.eq(i)
                }

                return jTasks.eq(0)
            },
            visibleEnd: function () {
                var iLeft = this.getLeft()
                var jTasks = this.getTasks()

                for (var i = 0; i < jTasks.size(); i++) {
                    if (jTasks.eq(i).position().left + jTasks.eq(i).outerWidth(true) + iLeft > this.getBarWidth()) return jTasks.eq(i)
                }

                return jTasks.eq(jTasks.size() - 1)
            },
            getTasks: function () {
                return $taskList.find('> li')
            },
            tasksW: function (jTasks) {
                var iW = 0

                jTasks.each(function () {
                    iW += $(this).outerWidth(true)
                })

                return iW
            },
            getBarWidth: function () {
                return $taskBar.innerWidth()
            },
            getCurrent: function () {
                return $taskList.find('li.' + taskSelected)
            }
        }

        return tools
    }

    Taskbar.prototype.contextmenu = function ($obj) {
        var that = this

        $obj.contextmenu({
            id: 'dialogCM',
            bindings: {
                reload: function (t) {
                    t.dialog('refresh', that.options.id)
                },
                closeCurrent: function (t, m) {
                    var $obj = t.isTag('li') ? t : that.tools.getCurrent()

                    $obj.find('.close').trigger('click')
                },
                closeOther: function (t, m) {
                    var $tasks = $taskList.find('> li').not(t)

                    $tasks.each(function (i) {
                        $(this).find('.close').trigger('click')
                    })
                },
                closeAll: function (t, m) {
                    var $tasks = that.tools.getTasks()

                    $tasks.each(function (i) {
                        $(this).find('.close').trigger('click')
                    })
                }
            },
            ctrSub: function (t, m) {
                var mCur = m.find('[rel="closeCurrent"]')
                var mOther = m.find('[rel="closeOther"]')

                if (!that.tools.getCurrent().length) {
                    mCur.addClass(taskDisabled)
                    mOther.addClass(taskDisabled)
                } else {
                    if (that.tools.getTasks().size() == 1)
                        mOther.addClass(taskDisabled)
                }
            }
        })
    }

    Taskbar.prototype.closeDialog = function (task) {
        var $task = (typeof task === 'string') ? this.getTask(task) : task

        if (!$task || !$task.length) return

        $task.remove()
        if (!this.tools.getTasks().size()) {
            this.hide()
        }
        this.tools.scrollCurrent()
        this.$element.removeData('bjui.taskbar')
    }

    Taskbar.prototype.minimize = function (dialog) {
        var that = this,
            datas = $('body').data('bjui.dialog'),
            $dialog = (typeof dialog === 'string') ? datas[dialog].dialog : dialog,
            $task = this.getTask($dialog.data('options').id)

        $resizable.css({
            top: $dialog.css('top'),
            left: $dialog.css('left'),
            height: $dialog.css('height'),
            width: $dialog.css('width')
        }).show().animate({ top: $(window).height() - 60, left: $task.position().left, width: $task.outerWidth(), height: $task.outerHeight() }, 250, function () {
            $(this).hide()
            that.inactive($task)
        })
    }

    /**
     * @param {Object} id or dialog
     */
    Taskbar.prototype.restoreDialog = function ($dialog) {
        var $task = this.getTask($dialog.data('options').id)

        $resizable.css({ top: $(window).height() - 60, left: $task.position().left, height: $task.outerHeight(), width: $task.outerWidth() })
            .show()
            .animate({ top: $dialog.css('top'), left: $dialog.css('left'), width: $dialog.css('width'), height: $dialog.css('height') }, 250, function () {
                $(this).hide()

                $dialog.show().trigger('click.bjui.taskbar.restore')
            })

        this.switchTask($task)
    }

    /**
     * @param {Object} id
     */
    Taskbar.prototype.inactive = function (task) {
        var $task = (typeof task === 'string') ? this.getTask(task) : task

        $task.removeClass(taskSelected)
    }

    Taskbar.prototype.scrollLeft = function () {
        var $task = this.tools.visibleStart()

        this.tools.scrollTask($task)
    }

    Taskbar.prototype.scrollRight = function () {
        var $task = this.tools.visibleEnd()

        this.tools.scrollTask($task)
    }

    Taskbar.prototype.scrollCurrent = function ($task) {
        this.tools.scrollTask($task)
    }

    /**
     * @param {Object} id or $task
     */
    Taskbar.prototype.switchTask = function (task) {
        this.tools.getCurrent().removeClass(taskSelected)
        var $task = (typeof task === 'string') ? this.getTask(task) : task

        $task.addClass(taskSelected)
    }

    Taskbar.prototype.getTask = function (id) {
        return $taskList.find('#task-' + id)
    }

    Taskbar.prototype.changeTitle = function (id, title) {
        var $task = this.getTask(id)

        if ($task && title) $task.find('.title').html(title)
    }

    Taskbar.prototype.show = function () {
        if ($taskBar.is(':hidden')) $taskBar.show().animate({ bottom: 0 }, 500)
    }

    Taskbar.prototype.hide = function () {
        if ($taskBar.is(':visible')) $taskBar.animate({ bottom: -50 }, 500, function () { $taskBar.hide() })
    }

    // TASKBAR PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments
        var property = option

        return this.each(function () {
            var $this = $(this)
            var options = $.extend({}, Taskbar.DEFAULTS, $this.data(), typeof option === 'object' && option)
            var data = $this.data('bjui.taskbar')

            if (!data) $this.data('bjui.taskbar', (data = new Taskbar(this, options)))
            else if (data.options.id != options.id) $this.data('bjui.taskbar', (data = new Taskbar(this, options)))

            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.taskbar

    $.fn.taskbar = Plugin
    $.fn.taskbar.Constructor = Taskbar

    // TASKBAR NO CONFLICT
    // =================

    $.fn.taskbar.noConflict = function () {
        $.fn.taskbar = old
        return this
    }

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-ajax.js  V1.31
 * @author K'naan 
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-ajax.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // BJUIAJAX GLOBAL ELEMENTS
    // ======================

    var autorefreshTimer

    // BJUIAJAX CLASS DEFINITION
    // ======================

    var Bjuiajax = function (element, options) {
        var $this = this

        this.$element = $(element)
        this.options = options
        this.tools = this.TOOLS()
    }

    Bjuiajax.DEFAULTS = {
        okalert: true,
        reload: true,
        loadingmask: true,
        gridrefreshflag: true
    }

    Bjuiajax.NAVTAB = 'navtab'

    Bjuiajax.prototype.TOOLS = function () {
        var that = this
        var tools = {
            getTarget: function () {
                if (that.$element.closest('.navtab-panel').length) return Bjuiajax.NAVTAB
                else return 'dialog'
            }
        }

        return tools
    }

    Bjuiajax.prototype.ajaxdone = function (json) {
        if (json[BJUI.keys.statusCode] == BJUI.statusCode.error) {
            if (json[BJUI.keys.message]) BJUI.alertmsg('error', json[BJUI.keys.message])
        } else if (json[BJUI.keys.statusCode] == BJUI.statusCode.timeout) {
            BJUI.alertmsg('info', (json[BJUI.keys.message] || BJUI.regional.sessiontimeout))
            BJUI.loadLogin()
        } else {
            if (json[BJUI.keys.message]) BJUI.alertmsg('correct', json[BJUI.keys.message])
        }
    }

    Bjuiajax.prototype.ajaxerror = function (xhr, ajaxOptions, thrownError) {
        var msg = xhr.responseText, that = this, options = that.$element.data('bjui.ajax.options') || that.options, failCallback = options.failCallback

        if (typeof msg === 'string' && msg.startsWith('{')) {
            this.ajaxdone(msg.toObj())
        } else {
            BJUI.alertmsg('error', '<div>Http status: ' + xhr.status + ' ' + xhr.statusText + '</div>'
                + '<div>ajaxOptions: ' + ajaxOptions + ' </div>'
                + '<div>thrownError: ' + thrownError + ' </div>'
                + '<div>' + msg + '</div>')
        }

        if (failCallback) {
            if (typeof failCallback === 'string')
                failCallback = failCallback.toFunc()
            if (typeof failCallback === 'function')
                failCallback.apply(that, [msg, options])
            else
                BJUI.debug('The callback function \'failCallback\' is incorrect: ' + failCallback)
        }
    }

    Bjuiajax.prototype.ajaxcallback = function (json) {
        var that = this, options = that.options,
            okCallback = options.okCallback, errCallback = options.errCallback, okAfterCallback = options.okAfterCallback,
            tabids = [], dialogids = [], divids = [], datagrids = []

        var okFunc = function () {
            if (typeof okCallback === 'string')
                okCallback = okCallback.toFunc()
            if (typeof okCallback === 'function')
                okCallback.apply(that, [json, options])
            else
                BJUI.debug('The callback function \'okCallback\' is incorrect: ' + okCallback)
        },
            okAfterFunc = function () {
                if (typeof okAfterCallback === 'string')
                    okAfterCallback = okAfterCallback.toFunc()
                if (typeof okAfterCallback === 'function')
                    okAfterCallback.apply(that, [json, options])
                else
                    BJUI.debug('The callback function \'okAfterCallback\' is incorrect: ' + okAfterCallback)
            },
            errFunc = function () {
                if (typeof errCallback === 'string')
                    errCallback = errCallback.toFunc()
                if (typeof errCallback === 'function')
                    errCallback.apply(that, [json, options])
                else
                    BJUI.debug('The callback function \'errCallback\' is incorrect: ' + errCallback)
            }

        if (typeof json === 'string')
            json = json.toObj()

        if (options.okalert)
            that.ajaxdone(json)

        if (!json[BJUI.keys.statusCode]) {
            if (okCallback) {
                okFunc()
                return
            }
        }
        if (json[BJUI.keys.statusCode] == BJUI.statusCode.ok) {
            if (okCallback) {
                okFunc()
                return
            }

            if (json.tabid)
                $.merge(tabids, json.tabid.split(','))
            if (options.tabid)
                $.merge(tabids, options.tabid.split(','))

            if (json.dialogid)
                $.merge(dialogids, json.dialogid.split(','))
            if (options.dialogid)
                $.merge(dialogids, options.dialogid.split(','))

            if (json.divid)
                $.merge(divids, json.divid.split(','))
            if (options.divid)
                $.merge(divids, options.divid.split(','))

            if (json.datagrid)
                $.merge(datagrids, json.datagrid.split(','))
            if (options.datagrid)
                $.merge(datagrids, options.datagrid.split(','))

            // refresh
            if (tabids.length) {
                $.unique(tabids)
                setTimeout(function () { BJUI.navtab('reloadFlag', tabids.join(',')) }, 100)
            }
            if (dialogids.length) {
                $.unique(dialogids)
                setTimeout(function () { BJUI.dialog('refresh', dialogids.join(',')) }, 100)
            }
            if (divids.length) {
                $.unique(divids)
                setTimeout(function () { that.refreshdiv(divids.join(',')) }, 100)
            }
            if (datagrids.length) {
                setTimeout(function () {
                    $.each(datagrids, function (i, n) {
                        $('#' + n.trim()).datagrid('refresh', options.gridrefreshflag)
                    })
                }, 100)
            }

            if (that.$target == $.CurrentNavtab) {
                that.navtabcallback(json)
            } else if (that.$target == $.CurrentDialog) {
                that.dialogcallback(json)
            } else {
                that.divcallback(json)
            }

            if (okAfterCallback) {
                okAfterFunc()
            }
        } else {
            if (errCallback) {
                errFunc()
            }
        }
    }

    Bjuiajax.prototype.divcallback = function (json, $target, options) {
        var that = this, options = that.options,
            forward = json.forward || options.forward || null,
            forwardConfirm = json.forwardConfirm || options.forwardConfirm || null


        if (options.reload && !forward) {
            that.refreshlayout()
        }

        if (options.reloadNavtab)
            setTimeout(function () { BJUI.navtab('refresh') }, 100)

        if (forward) {
            var _forward = function () {
                $.extend(options, { url: forward })
                that.refreshlayout()
            }

            if (forwardConfirm) {
                BJUI.alertmsg('confirm', forwardConfirm, {
                    okCall: function () { _forward() }
                })
            } else {
                _forward()
            }
        }
    }

    Bjuiajax.prototype.navtabcallback = function (json) {
        var that = this, options = that.options,
            closeCurrent = json.closeCurrent || options.closeCurrent || false,
            forward = json.forward || options.forward || null,
            forwardConfirm = json.forwardConfirm || options.forwardConfirm || null

        if (closeCurrent && !forward)
            BJUI.navtab('closeCurrentTab')
        else if (options.reload && !forward)
            setTimeout(function () { BJUI.navtab('refresh') }, 100)

        if (forward) {
            var _forward = function () {
                BJUI.navtab('reload', { url: forward })
            }

            if (forwardConfirm) {
                BJUI.alertmsg('confirm', forwardConfirm, {
                    okCall: function () { _forward() },
                    cancelCall: function () { if (closeCurrent) { BJUI.navtab('closeCurrentTab') } }
                })
            } else {
                _forward()
            }
        }
    }

    Bjuiajax.prototype.dialogcallback = function (json) {
        var that = this, options = that.options,
            closeCurrent = json.closeCurrent || options.closeCurrent || false,
            forward = json.forward || options.forward || null,
            forwardConfirm = json.forwardConfirm || options.forwardConfirm || null

        if (closeCurrent && !forward)
            BJUI.dialog('closeCurrent')
        else if (options.reload && !forward)
            setTimeout(function () { BJUI.dialog('refresh') }, 100)

        if (options.reloadNavtab)
            setTimeout(function () { BJUI.navtab('refresh') }, 100)
        if (forward) {
            var _forward = function () {
                BJUI.dialog('reload', { url: forward })
            }

            if (forwardConfirm) {
                BJUI.alertmsg('confirm', json.forwardConfirm, {
                    okCall: function () { _forward() },
                    cancelCall: function () { if (closeCurrent) { BJUI.dialog('closeCurrent') } }
                })
            } else {
                _forward()
            }
        }
    }

    Bjuiajax.prototype.doajax = function (options) {
        var that = this, $target, $element = that.$element

        if (!options) options = {}
        if (!options.loadingmask) options.loadingmask = false

        options = $.extend({}, Bjuiajax.DEFAULTS, typeof options === 'object' && options)
        that.options = options

        if (options.target) {
            if (options.target instanceof jQuery)
                $target = options.target
            else
                $target = $(options.target)
        } else {
            if ($element[0] !== $('body')[0]) {
                $target = $element.closest('.bjui-layout')
            }
        }

        if (!$target || !$target.length)
            $target = $.CurrentDialog || $.CurrentNavtab

        that.$target = $target

        if (!options.url) {
            BJUI.debug('BJUI.ajax: \'doajax\' method: the url is undefined!')
            return
        } else {
            options.url = decodeURI(options.url).replacePlh($target)

            if (!options.url.isFinishedTm()) {
                BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('BJUI.ajax: \'doajax\' method: The url is incorrect: ' + options.url)
                return
            }

            options.url = encodeURI(options.url)
        }

        var callback = options.callback && options.callback.toFunc()
        var todo = function () {
            if (!options.callback)
                options.callback = $.proxy(function (data) { that.ajaxcallback(data) }, that)

            $target.data('bjui.ajax.options', options).doAjax(options)
        }

        if (options.confirmMsg) {
            BJUI.alertmsg('confirm', options.confirmMsg,
                {
                    okCall: function () {
                        todo()
                    }
                }
            )
        } else {
            todo()
        }
    }

    Bjuiajax.prototype._ajaxform = function ($form, options) {
        var that = this,
            $target = null,
            beforeSubmit = options.beforeSubmit,
            callback = options.callback,
            isSubmit = false,
            enctype

        if ($form.data('holdSubmit')) {
            return
        }
        // beforeSubmit
        if (beforeSubmit) {
            if (typeof beforeSubmit === 'string')
                beforeSubmit = beforeSubmit.toFunc()
            if (typeof beforeSubmit === 'function') {
                if (!beforeSubmit.apply(that, [$form])) {
                    return
                }
            } else {
                BJUI.debug('BJUI.ajax: \'ajaxform\' options \'beforeSubmit\': Not a function!')
                return
            }
        }

        // for webuploader
        var $uploaders = $form.find('input[data-toggle="webuploader"]'), requiredMsg, error4Uploader = false

        if ($uploaders.length && WebUploader) {
            $uploaders.each(function (i, n) {
                var uploader = $(this).data('webuploader'), $lis = $(this).data('webuploader.wrap').find('.filelist > li'), fileCount = $lis.length, waitCount = fileCount - $lis.filter('.uploaded').length

                if (waitCount && uploader) {
                    if (waitCount !== uploader.getStats().successNum) {
                        // 自动上传
                        uploader.upload()
                        // 上传完成后触发
                        uploader.on('uploadFinished', function () {
                            $form.submit()
                        })

                        error4Uploader = true
                        return false
                    }
                }
                if (uploader && uploader.options.required) {
                    if (!fileCount) {
                        requiredMsg = uploader.options.requiredMsg || '请上传图片！'
                        return false
                    }
                }
            })
        }

        if (requiredMsg) {
            BJUI.alertmsg('info', requiredMsg)
            return
        }
        if (error4Uploader) {
            BJUI.alertmsg('info', '表单将在图片上传完成后自动提交！')
            return
        }

        enctype = $form.attr('enctype')

        // target
        if (!options.target)
            options.target = $form.closest('.bjui-layout')
        if (options.target) {
            $target = options.target

            if (!($target instanceof jQuery))
                $target = $($target)
        }
        if (!$target || !$target.length)
            $target = $.CurrentDialog || $.CurrentNavtab

        that.$target = $target

        options.url = options.url || $form.attr('action')
        // for ie8
        if (BJUI.isIE(8) && !options.type) {
            if (!$form[0].getAttributeNode('method').specified) options.type = 'POST'
        }
        options.type = options.type || $form.attr('method') || 'POST'

        $.extend(that.options, options)

        if (callback) callback = callback.toFunc()

        var successFn = function (data, textStatus, jqXHR) {
            callback ? callback.apply(that, [data, $form]) : $.proxy(that.ajaxcallback(data), that)
        }
        var _submitFn = function () {
            var op = { loadingmask: that.options.loadingmask, type: that.options.type, url: that.options.url, callback: successFn }

            if (enctype && enctype == 'multipart/form-data') {
                if (window.FormData) {
                    $.extend(op, { data: new FormData($form[0]), contentType: false, processData: false })
                } else {
                    $.extend(op, { data: $form.serializeArray(), files: $form.find(':file'), iframe: true, processData: false })
                }
            } else {
                $.extend(op, { data: $form.serializeArray() })
            }

            if (that.options.ajaxTimeout) op.ajaxTimeout = that.options.ajaxTimeout

            $form.doAjax(op)
        }

        if (that.options.confirmMsg) {
            BJUI.alertmsg('confirm', that.options.confirmMsg, { okCall: _submitFn })
        } else {
            _submitFn()
        }
    }

    Bjuiajax.prototype.ajaxform = function (option) {
        var that = this,
            options = $.extend({}, typeof option === 'object' && option),
            $element = that.$element,
            $form = null,
            valiOpts = {
                validClass: 'ok',
                msgClass: 'n-bottom',
                theme: 'red_bottom_effect_grid'
            }

        // form
        if ($element[0] === $('body')[0]) {
            $form = options.form

            if (!($form instanceof jQuery))
                $form = $(options.form)
        } else {
            $form = $element
        }
        if (!$form || !$form.length || !($form.isTag('form'))) {
            BJUI.debug('BJUI.ajax: \'ajaxform\' method: Not set the form!')
            return
        }

        if (typeof options.validate === 'undefined')
            options.validate = true

        if ($.fn.validator && options.validate) {
            if (!$form.data('validator'))
                $form.validator(valiOpts)

            $form
                .on('valid.form', function (e) {
                    that._ajaxform($form, options)
                })
                .on('invalid.form', function (e, form, errors) {
                    if (options.alertmsg) BJUI.alertmsg('error', BJUI.regional.validatemsg.replaceMsg(errors.length))
                })

            $form.trigger('validate')
        } else {
            that._ajaxform($form, options)
        }
    }

    Bjuiajax.prototype._ajaxsearch = function ($form, options) {
        var that = this,
            $target = null,
            beforeSubmit = options.beforeSubmit,
            callback = options.callback,
            isSubmit = false,
            enctype

        if ($form.data('holdSubmit')) {
            return
        }
        // beforeSubmit
        if (beforeSubmit) {
            if (typeof beforeSubmit === 'string')
                beforeSubmit = beforeSubmit.toFunc()
            if (typeof beforeSubmit === 'function') {
                if (!beforeSubmit.apply(that, [$form])) {
                    return
                }
            } else {
                BJUI.debug('BJUI.ajax: \'ajaxform\' options \'beforeSubmit\': Not a function!')
                return
            }
        }
        // pagination
        if ($form.data('bjui.paging')) {
            if (!options.data)
                options.data = {}

            $.extend(options.data, $form.data('bjui.paging'))
        }
        // search for datagrid
        if (options.searchDatagrid) {
            var $datagrid = options.searchDatagrid

            if (!($datagrid instanceof jQuery)) {
                $datagrid = $($datagrid)
            }

            if (!$datagrid || !$datagrid.length || !$datagrid.data('bjui.datagrid')) {
                BJUI.debug('BJUI.ajax: \'ajaxsearch\' method: The options \'searchDatagrid\' is not incorrect!')
                return
            }

            $form.data('holdSubmit', true)

            $datagrid.datagrid('filter', $form.serializeJson())

            $datagrid.on('afterLoad.bjui.datagrid', function () {
                $form.data('holdSubmit', false)
            })

            return
        }
        // target
        if (!options.target)
            options.target = $form.closest('.bjui-layout')
        if (options.target) {
            $target = options.target

            if (!($target instanceof jQuery))
                $target = $($target)
        }
        if (!$target || !$target.length)
            $target = $.CurrentDialog || $.CurrentNavtab

        that.$target = $target

        options.url = options.url || $form.attr('action')
        // for ie8
        if (BJUI.isIE(8) && !options.type) {
            if (!$form[0].getAttributeNode('method').specified) options.type = 'POST'
        }
        options.type = options.type || $form.attr('method') || 'POST'

        if (!options.url) {
            BJUI.debug('BJUI.ajax: \'ajaxsearch\' method: The form\'s action or url is undefined!')
            return
        } else {
            options.url = decodeURI(options.url).replacePlh($form.closest('.unitBox'))

            if (!options.url.isFinishedTm()) {
                BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('BJUI.ajax: \'ajaxsearch\' method: The form\'s action or url is incorrect: ' + options.url)
                return
            }

            options.url = encodeURI(options.url)
        }

        $.extend(that.options, options)

        if ($target.hasClass('bjui-layout')) {
            var data = $.extend($form.serializeJson(), options.data)

            that.reloadlayout({ target: $target[0], type: that.options.type, url: that.options.url, data: data, loadingmask: that.options.loadingmask })
        } else {
            options.form = $form
            if ($target[0] === ($.CurrentNavtab)[0]) {
                BJUI.navtab('reloadForm', that.options.clearQuery, options)
            } else {
                BJUI.dialog('reloadForm', that.options.clearQuery, options)
            }
        }
    }

    Bjuiajax.prototype.ajaxsearch = function (option) {
        var that = this,
            options = $.extend({}, typeof option === 'object' && option),
            $element = that.$element,
            $form = null,
            valiOpts = {
                validClass: 'ok',
                msgClass: 'n-bottom',
                theme: 'red_bottom_effect_grid'
            }

        // form
        if ($element[0] === $('body')[0]) {
            $form = options.form

            if (!($form instanceof jQuery)) {
                $form = $(options.form)
            }
        } else {
            $form = $element
        }
        if (!$form || !$form.length || !($form.isTag('form'))) {
            BJUI.debug('BJUI.ajax: \'ajaxsearch\' method: Not set the form!')
            return
        }

        if (typeof options.validate === 'undefined')
            options.validate = true

        if ($.fn.validator && options.validate) {
            if (!$form.data('validator'))
                $form.validator(valiOpts)

            $form
                .on('valid.form', function (e) {
                    that._ajaxsearch($form, options)
                })
                .on('invalid.form', function (e, form, errors) {
                    if (options.alertmsg) BJUI.alertmsg('error', BJUI.regional.validatemsg.replaceMsg(errors.length))
                })

            $form.trigger('validate')
        } else {
            that._ajaxsearch($form, options)
        }
    }

    Bjuiajax.prototype.doload = function (option) {
        var that = this, $target = null, options = that.options

        $.extend(options, typeof option === 'object' && option)

        if (options.target) {
            $target = options.target

            if (!($target instanceof jQuery))
                $target = $($target)
        }

        if (!$target || !$target.length) {
            BJUI.debug('BJUI.ajax: \'doload\' method: Not set loaded container, like [data-target].')
            return
        }

        if (!options.url) {
            BJUI.debug('BJUI.ajax: \'doload\' method: The url is undefined!')
            return
        } else {
            options.url = decodeURI(options.url).replacePlh()

            if (!options.url.isFinishedTm()) {
                BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('BJUI.ajax: \'doload\' method: The url is incorrect: ' + options.url)
                return
            }

            options.url = encodeURI(options.url)
        }

        $target.removeData('bjui.clientPaging').data('options', options)
        that.reloadlayout(options)
    }

    Bjuiajax.prototype.refreshlayout = function (target) {
        var that = this, $target

        if (target) {
            $target = target

            if (!($target instanceof jQuery))
                $target = $($target)
        } else {
            $target = that.$target
        }

        if (!$target || !$target.length) {
            if (autorefreshTimer) clearInterval(autorefreshTimer)
            BJUI.debug('BJUI.ajax: \'refreshlayout\' method: No argument, can not refresh DIV.(parameters:[selector/jQuery object/element])')
            return
        }
        if ($target && $target.length) {
            if (!$target.data('options')) {
                BJUI.debug('BJUI.ajax: \'refreshlayout\' method: The target(DIV) is not a reload layout!')
                return
            }
            $target.removeData('bjui.clientPaging')
            that.reloadlayout($target.data('options'))
        }
    }

    Bjuiajax.prototype.reloadlayout = function (option) {
        var $target = null,
            options = $.extend({}, typeof option === 'object' && option),
            arefre = options && options.autorefresh && (isNaN(String(options.autorefresh)) ? 15 : options.autorefresh)

        if (options.target) {
            $target = options.target

            if (!($target instanceof jQuery))
                $target = $($target)
        }

        if (!$target || !$target.length) {
            if (autorefreshTimer) clearInterval(autorefreshTimer)
            BJUI.debug('BJUI.ajax: \'refreshlayout\' method: Not set loaded container, like [data-target].')
            return
        } else {
            if (!$target.data('options')) {
                BJUI.debug('BJUI.ajax: \'refreshlayout\' method: This target(DIV) is not initialized!')
                return
            }
            options = $.extend({}, $target.data('options'), options)
        }

        $target
            .addClass('bjui-layout')
            .data('options', options)
            .ajaxUrl({
                type: options.type, url: options.url, data: options.data, loadingmask: options.loadingmask, callback: function (html) {
                    if (BJUI.ui.clientPaging && $target.data('bjui.clientPaging'))
                        $target.pagination('setPagingAndOrderby', $target)
                    if (options.callback)
                        options.callback.apply(this, [$target, html])
                    if (autorefreshTimer)
                        clearInterval(autorefreshTimer)
                    if (arefre)
                        autorefreshTimer = setInterval(function () { $target.bjuiajax('refreshlayout', $target) }, arefre * 1000)
                    if (!$target.height()) {
                        $target.css('position', 'static')
                    }
                }
            })
    }

    Bjuiajax.prototype.refreshdiv = function (divid) {
        if (divid && typeof divid === 'string') {
            var arr = divid.split(',')

            for (var i = 0; i < arr.length; i++) {
                this.refreshlayout('#' + arr[i])
            }
        }
    }

    Bjuiajax.prototype.ajaxdownload = function (option) {
        var that = this, $target, options = $.extend({}, { loadingmask: false }, typeof option === 'object' && option)

        $.extend(that.options, options)

        if (options.target) {
            $target = options.target

            if (!($target instanceof jQuery))
                $target = $($target)
        }

        if (!$target || !$target.length)
            $target = $.CurrentDialog || $.CurrentNavtab

        that.$target = $target

        if (!options.url) {
            BJUI.debug('BJUI.ajax: \'ajaxdownload\' method: The url is undefined!')
            return
        } else {
            options.url = decodeURI(options.url).replacePlh($target)

            if (!options.url.isFinishedTm()) {
                BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('BJUI.ajax: \'ajaxdownload\' method: The url is incorrect: ' + options.url)
                return
            }
        }

        var todo = function () {
            var downloadOptions = {}

            downloadOptions.failCallback = function (responseHtml, url) {
                if (responseHtml.trim().startsWith('{')) responseHtml = responseHtml.toObj()
                that.ajaxdone(responseHtml)
                $target.trigger('bjui.ajaxError')
            }
            downloadOptions.prepareCallback = function (url) {
                if (options.loadingmask) {
                    $target.trigger('bjui.ajaxStart')
                }
            }
            downloadOptions.successCallback = function (url) {
                $target.trigger('bjui.ajaxStop')
            }

            if (options.type && !options.httpMethod)
                options.httpMethod = options.type

            $.extend(downloadOptions, options)

            if (!downloadOptions.data) downloadOptions.data = {}
            if (typeof downloadOptions.data.ajaxrequest === 'undefined') downloadOptions.data.ajaxrequest = true

            $.fileDownload(options.url, downloadOptions)
        }

        if (options.confirmMsg) {
            BJUI.alertmsg('confirm', options.confirmMsg, {
                okCall: function () {
                    todo()
                }
            })
        } else {
            todo()
        }
    }

    // BJUIAJAX PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments,
            property = option,
            ajax = 'bjui.ajax',
            $body = $('body')

        return this.each(function () {
            var $this = $(this),
                options = $.extend({}, Bjuiajax.DEFAULTS, typeof option === 'object' && option),
                data = $this.data(ajax),
                func

            if (!data) {
                data = new Bjuiajax(this, options)
            } else {
                if (this === $body[0]) {
                    data = new Bjuiajax(this, options)
                } else {
                    $.extend(data.options, typeof option === 'object' && option)
                }
            }

            $this.data(ajax, data)

            if (typeof property === 'string') {
                func = data[property.toLowerCase()]
                if ($.isFunction(func)) {
                    [].shift.apply(args)
                    if (!args) func()
                    else func.apply(data, args)
                }
            }
        })
    }

    var old = $.fn.bjuiajax

    $.fn.bjuiajax = Plugin
    $.fn.bjuiajax.Constructor = Bjuiajax

    // BJUIAJAX NO CONFLICT
    // =================

    $.fn.bjuiajax.noConflict = function () {
        $.fn.bjuiajax = old
        return this
    }

    // NOT SELECTOR
    // ==============

    BJUI.ajax = function () {
        Plugin.apply($('body'), arguments)
    }

    // BJUIAJAX DATA-API
    // ==============

    $(document).on(BJUI.eventType.initUI, function (e) {
        if ($.fn.validator) {
            $(e.target).find('form[data-toggle="ajaxform"], form[data-toggle="ajaxsearch"]').each(function () {
                var $this = $(this), data = $this.data(), options = data.options,
                    valiOpts = {
                        validClass: 'ok',
                        msgClass: 'n-bottom',
                        theme: 'red_bottom_effect_grid'
                    }

                if (options) {
                    if (typeof options === 'string') options = options.toObj()
                    if (typeof options === 'object') {
                        delete data.options
                        $.extend(data, options)
                    }
                }
                if (typeof data.validate === 'undefined')
                    data.validate = true
                if (!data.validate) {
                    valiOpts.ignore = '*'
                }

                $this
                    .validator(valiOpts)
                    .on('valid.form', function (e) {
                        BJUI.ajax('_' + data.toggle, $this, data)
                    })
                    .on('invalid.form', function (e, form, errors) {
                        if (data.alertmsg) BJUI.alertmsg('error', BJUI.regional.validatemsg.replaceMsg(errors.length))
                    })
            })
        }
    })

    $(document).on('submit.bjui.bjuiajax.data-api', 'form[data-toggle="ajaxform"], form[data-toggle="ajaxsearch"]', function (e) {
        if (!$.fn.validator) {
            var $this = $(this), data = $this.data(), options = data.options

            if (options) {
                if (typeof options === 'string') options = options.toObj()
                if (typeof options === 'object') {
                    delete data.options
                    $.extend(data, options)
                }
            }

            Plugin.call($this, '_' + data.toggle, $this, data)

            e.preventDefault()
        }
    })

    $(document).on('click.bjui.bjuiajax.data-api', '[data-toggle="ajaxload"]', function (e) {
        var $this = $(this), data = $this.data(), options = data.options

        if (options) {
            if (typeof options === 'string') options = options.toObj()
            if (typeof options === 'object') {
                delete data.options
                $.extend(data, options)
            }
        }

        if (!data.url) data.url = $this.attr('href')

        Plugin.call($this, 'doload', data)

        e.preventDefault()
    })

    $(document).on(BJUI.eventType.initUI, function (e) {
        $(e.target).find('[data-toggle="autoajaxload"]').each(function () {
            var $element = $(this), data = $this.data(), options = data.options

            if (options) {
                if (typeof options === 'string') options = options.toObj()
                if (typeof options === 'object') {
                    delete data.options
                    $.extend(data, options)
                }
            }

            data.target = this
            Plugin.call($element, 'doload', data)
        })
    })

    $(document).on('click.bjui.bjuiajax.data-api', '[data-toggle="refreshlayout"]', function (e) {
        var $this = $(this), target = $this.data('target')

        Plugin.call($this, 'refreshlayout', target)

        e.preventDefault()
    })

    $(document).on('click.bjui.bjuiajax.data-api', '[data-toggle="reloadlayout"]', function (e) {
        var $this = $(this), data = $this.data()

        Plugin.call($this, 'reloadlayout', data)

        e.preventDefault()
    })

    $(document).on('click.bjui.bjuiajax.data-api', '[data-toggle="doajax"]', function (e) {
        var $this = $(this), data = $this.data(), options = data.options

        if (options) {
            if (typeof options === 'string') options = options.toObj()
            if (typeof options === 'object') {
                delete data.options
                $.extend(data, options)
            }
        }

        if (!data.url) data.url = $this.attr('href')

        Plugin.call($this, 'doajax', data)

        e.preventDefault()
    })

    $(document).on('click.bjui.bjuiajax.data-api', '[data-toggle="ajaxdownload"]', function (e) {
        var $this = $(this), data = $this.data(), options = data.options

        if (options) {
            if (typeof options === 'string') options = options.toObj()
            if (typeof options === 'object') {
                delete data.options
                $.extend(data, options)
            }
        }
        if (!data.url) data.url = $this.attr('href')

        Plugin.call($this, 'ajaxdownload', data)

        e.preventDefault()
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-alertmsg.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-alertmsg.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // ALERTMSG GLOBAL ELEMENTS
    // ======================

    var $box, $alertbg, timer

    $(function () {
        var INIT_ALERTMSG = function () {
            $box = $(FRAG.alertBoxFrag).hide().html('')
            $alertbg = $(FRAG.alertBackground).hide().html('')
            $('body').append('<!-- alert msg box -->').append($box).append('<!-- alert msg box mask bg -->').append($alertbg)
        }

        INIT_ALERTMSG()
    })

    // ALERTMSG CLASS DEFINITION
    // ======================
    var Alertmsg = function (options) {
        this.options = options
        this.tools = this.TOOLS()
        this.clearTime = null
    }

    Alertmsg.DEFAULTS = {
        displayPosition: 'topcenter', // Optional 'topleft, topcenter, topright, middleleft, middlecenter, middleright, bottomleft, bottomcenter, bottomright'
        displayMode: 'slide',     // Optional 'none, fade, slide'
        autoClose: null,
        alertTimeout: 3000,
        mask: null,
        promptRequired: 'required',
        types: { error: 'error', info: 'info', warn: 'warn', correct: 'correct', confirm: 'confirm', prompt: 'prompt' },
        fas: { error: 'fa-times-circle', info: 'fa-info-circle', warn: 'fa-exclamation-circle', correct: 'fa-check-circle', confirm: 'fa-question-circle', prompt: 'fa-paper-plane-o' }
    }

    Alertmsg.prototype.TOOLS = function () {
        var that = this
        var tools = {
            getTitle: function (key) {
                return that.options.title || BJUI.regional.alertmsg.title[key]
            },
            keydownOk: function (event) {
                if (event.which == BJUI.keyCode.ENTER) {
                    event.data.target.trigger('click')
                    return false
                }
                return true
            },
            keydownEsc: function (event) {
                if (event.which == BJUI.keyCode.ESC) event.data.target.trigger('click')
            },
            openPosition: function () {
                var position = BJUI.alertMsg.displayPosition, mode = BJUI.alertMsg.displayMode, width = 460, height = $box.outerHeight(), startCss = {}, endCss = {}

                if (position) {
                    if (that.options.displayPosition && that.options.displayPosition != 'topcenter')
                        position = that.options.displayPosition
                } else {
                    position = that.options.displayPosition
                }

                if (mode) {
                    if (that.options.displayMode && that.options.displayMode != 'slide')
                        mode = that.options.displayMode
                } else {
                    mode = that.options.displayMode
                }

                switch (position) {
                    case 'topleft':
                        startCss = { top: 0 - height, left: 0, 'margin-left': 0 }
                        endCss = { top: 0 }

                        break
                    case 'topcenter':
                        startCss = { top: 0 - height }
                        endCss = { top: 0 }

                        break
                    case 'topright':
                        startCss = { top: 0 - height, left: 'auto', right: 0, 'margin-left': 0 }
                        endCss = { top: 0 }

                        break
                    case 'middleleft':
                        startCss = { top: '50%', left: 0 - width, 'margin-left': 0, 'margin-top': 0 - height / 2 }
                        endCss = { left: 0 }

                        break
                    case 'middlecenter':
                        startCss = { top: '0', 'margin-top': 0 - height / 2 }
                        endCss = { top: '50%' }

                        break
                    case 'middleright':
                        startCss = { top: '50%', left: 'auto', right: 0 - width, 'margin-top': 0 - height / 2 }
                        endCss = { right: 0 }

                        break
                    case 'bottomleft':
                        startCss = { top: 'auto', left: 0, bottom: 0 - height, 'margin-left': 0 }
                        endCss = { bottom: 0 }

                        break
                    case 'bottomcenter':
                        startCss = { top: 'auto', bottom: 0 - height }
                        endCss = { bottom: 0 }

                        break
                    case 'bottomright':
                        startCss = { top: 'auto', left: 'auto', right: 0, bottom: 0 - height, 'margin-left': 0 }
                        endCss = { bottom: 0 }

                        break
                }

                if (mode == 'slide') {
                    $box.css(startCss).show().animate(endCss, 500)
                } else if (mode == 'fade') {
                    startCss.opacity = 0.1
                    $box.css(startCss).css(endCss).show().animate({ opacity: 1 }, 500)
                } else {
                    $box.css(startCss).css(endCss).show()
                }
            },
            closePosition: function () {
                var position = BJUI.alertMsg.displayPosition, mode = BJUI.alertMsg.displayMode, width = 460, height = $box.outerHeight(), endCss = {}

                if (position) {
                    if (that.options.displayPosition && that.options.displayPosition != 'topcenter')
                        position = that.options.displayPosition
                } else {
                    position = that.options.displayPosition
                }

                if (mode) {
                    if (that.options.displayMode && that.options.displayMode != 'slide')
                        mode = that.options.displayMode
                } else {
                    mode = that.options.displayMode
                }

                switch (position) {
                    case 'topleft':
                        endCss = { top: 0 - height }

                        break
                    case 'topcenter':
                        endCss = { top: 0 - height }

                        break
                    case 'topright':
                        endCss = { top: 0 - height }

                        break
                    case 'middleleft':
                        endCss = { left: 0 - width }

                        break
                    case 'middlecenter':
                        endCss = { top: 0 - height }

                        break
                    case 'middleright':
                        endCss = { right: 0 - width }

                        break
                    case 'bottomleft':
                        endCss = { bottom: 0 - height }

                        break
                    case 'bottomcenter':
                        endCss = { bottom: 0 - height }

                        break
                    case 'bottomright':
                        endCss = { bottom: 0 - height }

                        break
                }

                if (mode == 'slide') {
                    $box.animate(endCss, 500, function () {
                        $alertbg.hide()
                        $(this).hide().empty()
                    })
                } else if (mode == 'fade') {
                    $box.animate({ opacity: 0 }, 500, function () {
                        $alertbg.hide()
                        $(this).hide().empty()
                    })
                } else {
                    $box.hide().remove()
                    $alertbg.hide()
                }
            },
            open: function (type, msg, buttons) {
                var tools = this, btnsHtml = '', $newbox, $btns, alertTimeout = BJUI.alertMsg.alertTimeout, input = ''

                if (buttons) {
                    for (var i = 0; i < buttons.length; i++) {
                        var sRel = buttons[i].call ? 'callback' : '',
                            sCls = buttons[i].cls ? buttons[i].cls : 'default',
                            sIco = (buttons[i].cls && buttons[i].cls == 'green') ? 'check' : 'close'

                        btnsHtml += FRAG.alertBtnFrag.replace('#btnMsg#', '<i class="fa fa-' + sIco + '"></i> ' + buttons[i].name).replace('#callback#', sRel).replace('#class#', sCls)
                    }
                }
                if (type == that.options.types.prompt) {
                    input = '<p style="padding-top:5px;"><input type="text" class="form-control" name="' + (that.options.promptname || 'prompt') + '" value=""></p>'
                }

                $newbox =
                    $(FRAG.alertBoxFrag.replace('#type#', type)
                        .replace('#fa#', that.options.fas[type])
                        .replace('#title#', this.getTitle(type))
                        .replace('#message#', msg)
                        .replace('#prompt#', input)
                        .replace('#btnFragment#', btnsHtml))
                        .hide()
                        .appendTo('body')

                if ($box && $box.length) $box.remove()
                $box = $newbox

                tools.openPosition()

                if (timer) {
                    clearTimeout(timer)
                    timer = null
                }

                if (that.options.mask == null) {
                    if (!(that.options.types.info == type || that.options.types.correct == type))
                        $alertbg.show()
                }
                if (that.options.autoClose == null) {
                    if (that.options.types.info == type || that.options.types.correct == type) {
                        if (alertTimeout) {
                            if (that.options.alertTimeout && that.options.alertTimeout != 3000)
                                alertTimeout = that.options.alertTimeout
                        } else {
                            alertTimeout = that.options.alertTimeout
                        }
                        timer = setTimeout(function () { tools.close() }, alertTimeout)
                    }
                }
                if (type == that.options.types.prompt) {
                    $box.find(':text').focus().val(that.options.promptval)
                }

                $btns = $box.find('.btn')

                $btns.each(function (i) {
                    $(this).on('click', $.proxy(function () {
                        if (type !== that.options.types.prompt || $btns.eq(i).hasClass('btn-red'))
                            that.tools.close()

                        var call = buttons[i].call

                        if (typeof call === 'string') call = call.toFunc()
                        if (typeof call === 'function') {
                            if (type == that.options.types.prompt) {
                                var $input = $box.find('input'), value = $input.val()

                                $input.focus(function () {
                                    $box.removeClass('has-error')
                                })

                                if (that.options.promptRequired === 'required' && !value) {
                                    $box.addClass('has-error')
                                } else {
                                    that.tools.close()
                                    call.call(that, value)
                                }
                            } else {
                                call.call()
                            }
                        }
                    }, that)
                    )

                    if (buttons[i].keyCode === BJUI.keyCode.ENTER) {
                        $(document).on('keydown.bjui.alertmsg.ok', { target: $btns.eq(i) }, tools.keydownOk)
                    }
                    if (buttons[i].keyCode === BJUI.keyCode.ESC) {
                        $(document).on('keydown.bjui.alertmsg.esc', { target: $btns.eq(i) }, tools.keydownEsc)
                    }
                })
            },
            alert: function (type, msg, btnoptions) {
                $.extend(that.options, typeof btnoptions === 'object' && btnoptions)

                var op = $.extend({}, { okName: BJUI.regional.alertmsg.btnMsg.ok, okCall: null }, that.options)
                var buttons = [
                    { name: op.okName, call: op.okCall, cls: 'default', keyCode: BJUI.keyCode.ENTER }
                ]

                this.open(type, msg, buttons)
            },
            close: function () {
                $(document).off('keydown.bjui.alertmsg.ok').off('keydown.bjui.alertmsg.esc')

                this.closePosition()
            }
        }

        return tools
    }

    Alertmsg.prototype.error = function (msg, btnoptions) {
        this.tools.alert(this.options.types.error, msg, btnoptions)
    }

    Alertmsg.prototype.info = function (msg, btnoptions) {
        this.tools.alert(this.options.types.info, msg, btnoptions)
    }

    Alertmsg.prototype.warn = function (msg, btnoptions) {
        this.tools.alert(this.options.types.warn, msg, btnoptions)
    }

    Alertmsg.prototype.ok = function (msg, btnoptions) {
        this.tools.alert(this.options.types.correct, msg, btnoptions)
    }

    Alertmsg.prototype.correct = function (msg, btnoptions) {
        this.tools.alert(this.options.types.correct, msg, btnoptions)
    }

    Alertmsg.prototype.confirm = function (msg, btnoptions) {
        $.extend(this.options, typeof btnoptions === 'object' && btnoptions)

        var op = $.extend({}, { okName: BJUI.regional.alertmsg.btnMsg.ok, okCall: null, cancelName: BJUI.regional.alertmsg.btnMsg.cancel, cancelCall: null }, this.options)
        var buttons = [
            { name: op.okName, call: op.okCall, cls: 'green', keyCode: BJUI.keyCode.ENTER },
            { name: op.cancelName, call: op.cancelCall, cls: 'red', keyCode: BJUI.keyCode.ESC }
        ]

        this.tools.open(this.options.types.confirm, msg, buttons)
    }

    Alertmsg.prototype.prompt = function (msg, btnoptions) {
        $.extend(this.options, typeof btnoptions === 'object' && btnoptions)

        var that = this, op = $.extend({}, { okName: BJUI.regional.alertmsg.btnMsg.ok, okCall: null, cancelName: BJUI.regional.alertmsg.btnMsg.cancel, cancelCall: null }, this.options)

        if (op.okCall == null && (typeof that.options.prompt === 'object' && that.options.prompt)) {
            op.okCall = function (val) {
                var data = {}

                data[(that.options.promptname || 'prompt')] = val

                if (!that.options.prompt.data)
                    that.options.prompt.data = data
                else
                    $.extend(that.options.prompt.data, data)

                BJUI.ajax('doajax', that.options.prompt)
            }
        }

        var buttons = [
            { name: op.okName, call: op.okCall, cls: 'green', keyCode: BJUI.keyCode.ENTER },
            { name: op.cancelName, call: op.cancelCall, cls: 'red', keyCode: BJUI.keyCode.ESC }
        ]

        this.tools.open(this.options.types.prompt, msg, buttons)
    }

    // ALERTMSG PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments,
            property = option,
            alertmsg = 'bjui.alertmsg',
            $body = $('body'),
            data = $body.data(alertmsg)

        return this.each(function () {
            var $this = $(this),
                options = $.extend({}, Alertmsg.DEFAULTS, typeof option === 'object' && option)

            if (!data) {
                data = new Alertmsg(options)
            } else {
                data.options = options
            }

            $body.data(alertmsg, data)

            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            }
        })
    }

    var old = $.fn.alertmsg

    $.fn.alertmsg = Plugin
    $.fn.alertmsg.Constructor = Alertmsg

    // ALERTMSG NO CONFLICT
    // =================

    $.fn.alertmsg.noConflict = function () {
        $.fn.alertmsg = old
        return this
    }

    // NOT SELECTOR
    // ==============

    BJUI.alertmsg = function () {
        Plugin.apply($('body'), arguments)
    }

    // NAVTAB DATA-API
    // ==============

    $(document).on('click.bjui.alertmsg.data-api', '[data-toggle="alertmsg"]', function (e) {
        var $this = $(this), data = $this.data(), options = data.options, type

        if (options) {
            if (typeof options === 'string') options = options.toObj()
            if (typeof options === 'object') {
                $.extend(data, options)
            }
        }

        type = data.type
        if (!type) return false
        if (!data.msg) {
            if (options.msg) data.msg = options.msg
        }

        Plugin.call($this, type, data.msg || type, data)

        e.preventDefault()
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-pagination.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-pagination.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // PAGINATION CLASS DEFINITION
    // ======================

    var Pagination = function (element, options) {
        this.$element = $(element)
        this.options = options
        this.tools = this.TOOLS()
    }

    Pagination.DEFAULTS = {
        width: '100%',
        pageSize: 30,
        selectPageSize: '30,60,90',
        pageCurrent: 1,
        total: 0,
        showPagenum: 5,
        form: null,
        callback: null
    }

    Pagination.prototype.TOOLS = function () {
        var that = this
        var tools = {
            getPageCount: function (pageSize, total) {
                return Math.ceil(total / pageSize)
            },
            getPageInterval: function (count, pageCurrent, showPageNum) {
                var half = Math.ceil(showPageNum / 2), limit = count - showPageNum,
                    start = pageCurrent > half ? Math.max(Math.min(pageCurrent - half, limit), 0) : 0,
                    end = pageCurrent > half ? Math.min((pageCurrent + half), count) : Math.min(showPageNum, count)

                if (end - start == showPageNum) end = end + 1
                if (end < showPageNum) end = end + 1
                if (start + 1 == end) end = end + 1

                return { start: start + 1, end: end }
            },
            // jump to page
            jumpPage: function (pageCurrent, pageSize) {
                var callback = that.options.callback

                if (typeof callback === 'string')
                    callback = callback.toFunc()

                if (pageCurrent) {
                    that.options.pageCurrent = pageCurrent
                }
                if (pageSize) {
                    that.options.pageSize = pageSize
                    that.pageCount = this.getPageCount(pageSize, that.options.total)

                    if (that.options.pageCurrent > that.pageCount)
                        that.options.pageCurrent = that.pageCount
                }

                if (callback && typeof callback === 'function') {
                    callback.call(that, that.options.pageCurrent, pageSize)
                } else {
                    this.setPaging(that.options.pageCurrent, pageSize, true)
                }

                that.$element.trigger('bjui.pagination.jump')
            },
            setPaging: function (pageCurrent, pageSize, isSubmit) {
                var $form = that.options.form, paging = {}

                if ($form) {
                    if (!($form instanceof jQuery)) {
                        $form = $($form)
                    }
                    if (!$form || !$form.length) {
                        BJUI.debug('BJUI.Pagination: The options \'form\' is not incorrect!')
                        return
                    }

                    paging[BJUI.pageInfo.pageCurrent] = pageCurrent
                    paging[BJUI.pageInfo.pageSize] = pageSize

                    $form.data('bjui.paging', paging)

                    if (isSubmit)
                        $form.trigger('submit')
                }
            }
        }
        return tools
    }

    Pagination.prototype.jumpPage = function (pageCurrent, pageSize) {
        var that = this, options = that.options, pageCount = that.pageCount

        if (pageCurrent && isNaN(pageCurrent)) return
        if (pageSize && isNaN(pageSize)) return
        if (pageCurrent) {
            pageCurrent = parseInt(pageCurrent, 10)

            if (pageCurrent < 1) pageCurrent = 1
            if (pageCurrent > pageCount) pageCurrent = pageCount
            if (pageCurrent == options.pageCurrent) return
        }
        if (pageSize) {
            pageSize = parseInt(pageSize, 10)
        }

        that.tools.jumpPage(pageCurrent || options.pageCurrent, pageSize || options.pageSize)
    }

    Pagination.prototype.init = function () {
        var that = this, tools = that.tools, options = that.options, pr = BJUI.regional.pagination, btnpaging = FRAG.gridPaging, pageNums = [], interval, selectPages = [], pagingHtml = BJUI.StrBuilder()

        that.pageCount = tools.getPageCount(options.pageSize, options.total)

        interval = tools.getPageInterval(that.pageCount, options.pageCurrent, options.showPagenum)

        for (var i = interval.start; i < interval.end; i++) {
            pageNums.push(FRAG.gridPageNum.replace('#num#', i).replace('#active#', (options.pageCurrent == i ? ' active' : '')))
        }

        btnpaging = BJUI.doRegional(btnpaging.replaceAll('#pageCurrent#', options.pageCurrent).replaceAll('#count#', options.pageCurrent + '/' + parseInt((that.pageCount || 0), 10)).replaceAll('#totalCount#', '共' + options.total + '条'), pr)

        pagingHtml
            .add('<div class="paging-content">')
            .add('<span></span><div class="paging-pagesize"><button type="button" class="btn-default btn-refresh" title="' + pr.refresh + '" data-icon="refresh"></button>')
            .add('<select data-toggle="selectpicker"></select>')
            .add('</div>')
            .add('<div class="paging-box">')
            .add(btnpaging.replace('#pageNumFrag#', pageNums.join('')))
            .add('</div>')
            .add('</div>')

        that.$element.addClass('bjui-paging-box').html(pagingHtml.toString())
        that.$element.find('> .paging-content').width(options.width).initui()

        tools.setPaging(options.pageCurrent, options.pageSize)

        //events
        var $select = that.$element.find('div.paging-pagesize > select'),
            $pagenum = that.$element.find('ul.pagination'),
            $pagetotal = $pagenum.find('> li.page-total'),
            $pagetotalCount = $pagenum.find('> li.page-totalCount'),
            $jumpto = $pagetotal.next(),
            $first = $jumpto.next(),
            $prev = $first.next(),
            $next = $prev.nextAll('.page-next'),
            $last = $next.next()

        var disablePrev = function () {
            $first.addClass('disabled')
            $prev.addClass('disabled')
        }
        var enablePrev = function () {
            $first.removeClass('disabled')
            $prev.removeClass('disabled')
        }
        var disableNext = function () {
            $next.addClass('disabled')
            $last.addClass('disabled')
        }
        var enableNext = function () {
            $next.removeClass('disabled')
            $last.removeClass('disabled')
        }
        var pageFirst = function () {
            disablePrev()
            enableNext()
        }
        var pageLast = function () {
            enablePrev()
            disableNext()
        }
        var setPageSize = function (pageSize) {
            $select.empty()

            if (!pageSize) pageSize = that.options.pageSize

            selectPages = options.selectPageSize.split(',')
            selectPages.push(String(pageSize))

            $.unique(selectPages).sort(function (a, b) { return a - b })

            var opts = []

            $.each(selectPages, function (i, n) {
                opts.push('<option value="' + n + '"' + (n == options.pageSize && 'selected') + '>' + pr.eachpage + n + '' + pr.unit + '</option>')
            })

            $select.html(opts.join('')).selectpicker('refresh')
        }

        if (options.pageCurrent == 1) pageFirst()
        if (options.pageCurrent == that.pageCount) {
            pageLast()
            if (options.pageCurrent == 1) disablePrev()
        }
        if (!options.total) disableNext()
        setPageSize()

        that.$element.on('click.bjui.pagination.pagenum', 'li.page-num', function (e) {
            var $num = $(this)

            if (!$num.hasClass('active')) {
                that.jumpPage($num.text())
            }

            e.preventDefault()
        }).on('click.bjui.pagination.refresh', 'button.btn-refresh', function () {
            that.jumpPage()
        }).on('bjui.pagination.jump', function (e) {
            var pageCurrent = that.options.pageCurrent, interval = tools.getPageInterval(that.pageCount, pageCurrent, options.showPagenum), pageNums = []

            for (var i = interval.start; i < interval.end; i++) {
                pageNums.push(FRAG.gridPageNum.replace('#num#', i).replace('#active#', (pageCurrent == i ? ' active' : '')))
            }

            $pagenum.find('> li.page-num').remove()
            $prev.after(pageNums.join(''))

            if (pageCurrent == 1) {
                pageFirst()
                if (pageCurrent == that.pageCount) disableNext()
                if (!that.options.total) disableNext()
            } else if (pageCurrent == that.pageCount) {
                pageLast()
            } else {
                enablePrev()
                enableNext()
            }

            $jumpto.find('input').val(pageCurrent)
            $pagetotal.find('span:first').html(pageCurrent + '/' + that.pageCount)
            $pagetotalCount.find('span:first').attr('title', pr.ttl + options.total + pr.unit).html(pr.ttl + options.total + pr.unit)
        }).on('bjui.pagination.pageSize', function (e, pageSize) {
            setPageSize(pageSize)
        }).on('change', 'div.paging-pagesize > select', function () {
            var pageSize = $(this).val()

            that.pageCount = tools.getPageCount(pageSize, options.total)

            that.jumpPage(null, pageSize)
        })

        $jumpto.find('input').on('keyup', function (e) {
            if (e.which === BJUI.keyCode.ENTER) {
                var page = $(this).val()

                if (page) that.jumpPage(page)
            }
        })

        $first.on('click', function () {
            if (that.options.pageCurrent > 1)
                that.jumpPage(1)
        })

        $prev.on('click', function () {
            if (that.options.pageCurrent > 1)
                that.jumpPage(that.options.pageCurrent - 1)
        })

        $next.on('click', function () {
            if (that.options.pageCurrent < that.pageCount)
                that.jumpPage(that.options.pageCurrent + 1)
        })

        $last.on('click', function () {
            if (that.options.pageCurrent < that.pageCount)
                that.jumpPage(that.pageCount)
        })
    }

    Pagination.prototype.destroy = function () {
        this.$element.removeData('bjui.pagination').empty()
    }

    // PAGINATION PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments,
            property = option

        return this.each(function () {
            var $this = $(this),
                options = $.extend({}, Pagination.DEFAULTS, $this.data(), typeof option === 'object' && option),
                data = $this.data('bjui.pagination')

            if (!data) $this.data('bjui.pagination', (data = new Pagination(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.pagination

    $.fn.pagination = Plugin
    $.fn.pagination.Constructor = Pagination

    // PAGINATION NO CONFLICT
    // =================

    $.fn.pagination.noConflict = function () {
        $.fn.pagination = old
        return this
    }

    // PAGINATION DATA-API
    // ==============

    $(document).on(BJUI.eventType.initUI, function (e) {
        $(e.target).find('[data-toggle="pagination"]').each(function () {
            var $this = $(this), data = $this.data(), options = data.options

            if (options) {
                if (typeof options === 'string') options = options.toObj()
                if (typeof options === 'object') {
                    delete data.options
                    $.extend(data, options)
                }
            }

            Plugin.call($this, data)
        })
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-util.date.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-util.date.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    var MONTH_NAMES = new Array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')

    var DAY_NAMES = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')

    function LZ(x) {
        return (x < 0 || x > 9 ? '' : '0') + x
    }

    /**
         * formatDate (date_object, format)
         * Returns a date in the output format specified.
         * The format string uses the same abbreviations as in parseDate()
         * @param {Object} date
         * @param {Object} format
         */
    function formatDate(date, format) {
        format = format + ''
        var result = ''
        var i_format = 0
        var c = ''
        var token = ''
        var y = date.getYear() + ''
        var M = date.getMonth() + 1
        var d = date.getDate()
        var E = date.getDay()
        var H = date.getHours()
        var m = date.getMinutes()
        var s = date.getSeconds()
        var yyyy, yy, MMM, MM, dd, hh, h, mm, ss, ampm, HH, H, KK, K, kk, k
        // Convert real date parts into formatted versions
        var value = {}

        if (y.length < 4) {
            y = '' + (y - 0 + 1900)
        }
        value['y'] = '' + y
        value['yyyy'] = y
        value['yy'] = y.substring(2, 4)
        value['M'] = M
        value['MM'] = LZ(M)
        value['MMM'] = MONTH_NAMES[M - 1]
        value['NNN'] = MONTH_NAMES[M + 11]
        value['d'] = d
        value['dd'] = LZ(d)
        value['E'] = DAY_NAMES[E + 7]
        value['EE'] = DAY_NAMES[E]
        value['H'] = H
        value['HH'] = LZ(H)

        if (!H) {
            value['h'] = 12
        } else if (H > 12) {
            value['h'] = H - 12
        } else {
            value['h'] = H
        }
        value['hh'] = LZ(value['h'])

        if (H > 11) {
            value['K'] = H - 12
        } else {
            value['K'] = H
        }
        value['k'] = H + 1
        value['KK'] = LZ(value['K'])
        value['kk'] = LZ(value['k'])

        if (H > 11) {
            value['a'] = 'PM'
        } else {
            value['a'] = 'AM'
        }
        value['m'] = m
        value['mm'] = LZ(m)
        value['s'] = s
        value['ss'] = LZ(s)

        while (i_format < format.length) {
            c = format.charAt(i_format)
            token = ''

            while (format.charAt(i_format) == c && i_format < format.length) {
                token += format.charAt(i_format++)
            }
            if (value[token] != null) {
                result += value[token]
            } else {
                result += token
            }
        }
        return result
    }

    function _isInteger(val) {
        return new RegExp(/^\d+$/).test(val)
    }

    function _getInt(str, i, minlength, maxlength) {
        for (var x = maxlength; x >= minlength; x--) {
            var token = str.substring(i, i + x)

            if (token.length < minlength) {
                return null
            }
            if (_isInteger(token)) {
                return token
            }
        }
        return null
    }

    /**
         * parseDate( date_string , format_string )
         * 
         * This function takes a date string and a format string. It matches
         * If the date string matches the format string, it returns the date. 
         * If it does not match, it returns 0.
         * @param {Object} val
         * @param {Object} format
         */
    function parseDate(val, format) {
        val = val + ''
        format = format + ''

        var i_val = 0
        var i_format = 0
        var c = ''
        var token = ''
        var token2 = ''
        var x, y
        var now = new Date(1900, 0, 1)
        var year = now.getYear()
        var month = now.getMonth() + 1
        var date = 1
        var hh = now.getHours()
        var mm = now.getMinutes()
        var ss = now.getSeconds()
        var ampm = ''

        while (i_format < format.length) {
            // Get next token from format string
            c = format.charAt(i_format)
            token = ''
            while (format.charAt(i_format) == c && i_format < format.length) {
                token += format.charAt(i_format++)
            }
            // Extract contents of value based on format token
            if (token == 'yyyy' || token == 'yy' || token == 'y') {
                if (token == 'yyyy') {
                    x = 4
                    y = 4
                }
                if (token == 'yy') {
                    x = 2
                    y = 2
                }
                if (token == 'y') {
                    x = 2
                    y = 4
                }
                year = _getInt(val, i_val, x, y)
                if (year == null) {
                    return 0
                }
                i_val += year.length
                if (year.length == 2) {
                    if (year > 70) {
                        year = 1900 + (year - 0)
                    } else {
                        year = 2e3 + (year - 0)
                    }
                }
            } else if (token == 'MMM' || token == 'NNN') {
                month = 0
                for (var i = 0; i < MONTH_NAMES.length; i++) {
                    var month_name = MONTH_NAMES[i]

                    if (val.substring(i_val, i_val + month_name.length).toLowerCase() == month_name.toLowerCase()) {
                        if (token == 'MMM' || token == 'NNN' && i > 11) {
                            month = i + 1
                            if (month > 12) {
                                month -= 12
                            }
                            i_val += month_name.length
                            break
                        }
                    }
                }
                if (month < 1 || month > 12) {
                    return 0
                }
            } else if (token == 'EE' || token == 'E') {
                for (var i = 0; i < DAY_NAMES.length; i++) {
                    var day_name = DAY_NAMES[i]

                    if (val.substring(i_val, i_val + day_name.length).toLowerCase() == day_name.toLowerCase()) {
                        i_val += day_name.length
                        break
                    }
                }
            } else if (token == 'MM' || token == 'M') {
                month = _getInt(val, i_val, token.length, 2)
                if (month == null || month < 1 || month > 12) {
                    return 0
                }
                i_val += month.length
            } else if (token == 'dd' || token == 'd') {
                date = _getInt(val, i_val, token.length, 2)
                if (date == null || date < 1 || date > 31) {
                    return 0
                }
                i_val += date.length
            } else if (token == 'hh' || token == 'h') {
                hh = _getInt(val, i_val, token.length, 2)
                if (hh == null || hh < 1 || hh > 12) {
                    return 0
                }
                i_val += hh.length
            } else if (token == 'HH' || token == 'H') {
                hh = _getInt(val, i_val, token.length, 2)
                if (hh == null || hh < 0 || hh > 23) {
                    return 0
                }
                i_val += hh.length
            } else if (token == 'KK' || token == 'K') {
                hh = _getInt(val, i_val, token.length, 2)
                if (hh == null || hh < 0 || hh > 11) {
                    return 0
                }
                i_val += hh.length
            } else if (token == 'kk' || token == 'k') {
                hh = _getInt(val, i_val, token.length, 2)
                if (hh == null || hh < 1 || hh > 24) {
                    return 0
                }
                i_val += hh.length
                hh--
            } else if (token == 'mm' || token == 'm') {
                mm = _getInt(val, i_val, token.length, 2)
                if (mm == null || mm < 0 || mm > 59) {
                    return 0
                }
                i_val += mm.length
            } else if (token == 'ss' || token == 's') {
                ss = _getInt(val, i_val, token.length, 2)
                if (ss == null || ss < 0 || ss > 59) {
                    return 0
                }
                i_val += ss.length
            } else if (token == 'a') {
                if (val.substring(i_val, i_val + 2).toLowerCase() == 'am') {
                    ampm = 'AM'
                } else if (val.substring(i_val, i_val + 2).toLowerCase() == 'pm') {
                    ampm = 'PM'
                } else {
                    return 0
                }
                i_val += 2
            } else {
                if (val.substring(i_val, i_val + token.length) != token) {
                    return 0
                } else {
                    i_val += token.length
                }
            }
        }
        // If there are any trailing characters left in the value, it doesn't match
        if (i_val != val.length) {
            return 0
        }
        // Is date valid for month?
        if (month == 2) {
            // Check for leap year
            if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
                // leap year
                if (date > 29) {
                    return 0
                }
            } else {
                if (date > 28) {
                    return 0
                }
            }
        }
        if (month == 4 || month == 6 || month == 9 || month == 11) {
            if (date > 30) {
                return 0
            }
        }
        // Correct hours value
        if (hh < 12 && ampm == 'PM') {
            hh = hh - 0 + 12
        } else if (hh > 11 && ampm == 'AM') {
            hh -= 12
        }

        return new Date(year, month - 1, date, hh, mm, ss)
    }

    Date.prototype.formatDate = function (dateFmt) {
        return formatDate(this, dateFmt)
    }

    String.prototype.parseDate = function (dateFmt) {
        if (this.length < dateFmt.length) {
            dateFmt = dateFmt.slice(0, this.length)
        }
        return parseDate(this, dateFmt)
    }

    /**
     * replaceTmEval('{1+2}-{2-1}')
     */
    function replaceTmEval(data) {
        return data.replace(RegExp('({[A-Za-z0-9_+-]*})', 'g'), function ($1) {
            return eval('(' + $1.replace(/[{}]+/g, '') + ')')
        })
    }

    /**
     * dateFmt:%y-%M-%d
     * %y-%M-{%d+1}
     * ex: new Date().formatDateTm('%y-%M-{%d-1}')
     *     new Date().formatDateTm('2012-1')
     */
    Date.prototype.formatDateTm = function (dateFmt) {
        var y = this.getFullYear()
        var m = this.getMonth() + 1
        var d = this.getDate()
        var sDate = dateFmt.replaceAll('%y', y).replaceAll('%M', m).replaceAll('%d', d)

        sDate = replaceTmEval(sDate)

        var _y = 1900, _m = 0, _d = 1
        var aDate = sDate.split('-')

        if (aDate.length > 0) _y = aDate[0]
        if (aDate.length > 1) _m = aDate[1] - 1
        if (aDate.length > 2) _d = aDate[2]

        return new Date(_y, _m, _d).formatDate('yyyy-MM-dd')
    }

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-datepicker.js  V1.31
 * reference: util.date.js
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-datepicker.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // DATEPICKER GLOBAL ELEMENTS
    // ======================

    var $box, $main, $prev, $next, $year, $month, $time, $timeinps, $spinner, $hh, $mm, $ss, $tm, $close, $days, $dayNames, $clearBtn, $okBtn

    $(function () {
        var INIT_DATEPICKER = function () {
            var cp = BJUI.regional.datepicker
            var calendar = FRAG.calendarFrag
                .replace('#close#', cp.close)
                .replace('#prev#', cp.prev)
                .replace('#next#', cp.next)
                .replace('#clear#', cp.clear)
                .replace('#ok#', cp.ok)

            $box = $(calendar).hide()
            $('body').append('<!-- datepicker -->').append($box)
            $main = $box.find('> .main')
            $prev = $box.find('a.prev')
            $next = $box.find('a.next')
            $year = $box.find('select[name=year]')
            $month = $box.find('select[name=month]')
            $time = $box.find('.time')
            $timeinps = $time.find(':text')
            $spinner = $time.find('ul > li')
            $hh = $time.find('.hh')
            $mm = $time.find('.mm')
            $ss = $time.find('.ss')
            $tm = $main.find('> .tm')
            $close = $box.find('.close')
            $days = $main.find('> .body > .days')
            $dayNames = $main.find('> .body > .dayNames')
            $clearBtn = $box.find('.clearBtn')
            $okBtn = $box.find('.okBtn')

            //regional
            var dayNames = '', dr = BJUI.regional.datepicker

            $.each(dr.dayNames, function (i, v) {
                dayNames += '<dt>' + v + '</dt>'
            })
            $dayNames.html(dayNames)
            $.each(dr.monthNames, function (i, v) {
                var m = i + 1

                $month.append('<option value="' + m + '">' + v + '</option>')
            })

            $box.on('selectstart', function () { return false })
        }

        INIT_DATEPICKER()
    })

    // DATEPICKER CLASS DEFINITION
    // ======================
    var Datepicker = function (element, options) {
        this.$element = $(element)
        this.options = options
        this.tools = this.TOOLS()
        this.$dateBtn = null

        // minDate、maxDate
        var now = new Date()

        this.options.minDate = now.formatDateTm(this.options.minDate)
        this.options.maxDate = now.formatDateTm(this.options.maxDate)

        //events
        this.events = {
            focus_time: 'focus.bjui.datepicker.time',
            click_prev: 'click.bjui.datepicker.prev',
            click_next: 'click.bjui.datepicker.next',
            click_ok: 'click.bjui.datepicker.ok',
            click_days: 'click.bjui.datepicker.days',
            click_clear: 'click.bjui.datepicker.clear',
            click_close: 'click.bjui.datepicker.close',
            click_tm: 'click.bjui.datepicker.tm',
            click_spinner: 'click.bjui.datepicker.spinner',
            mousedown_sp: 'mousedown.bjui.datepicker.spinner',
            mouseup_sp: 'mouseup.bjui.datepicker.spinner',
            change_ym: 'change.bjui.datepicker.ym',
            click_time: 'click.bjui.datepicker.time',
            keydown_time: 'keydown.bjui.datepicker.time',
            keyup_time: 'keyup.bjui.datepicker.time'
        }
    }

    Datepicker.DEFAULTS = {
        pattern: 'yyyy-MM-dd',
        minDate: '1900-01-01',
        maxDate: '2099-12-31',
        mmStep: 1,
        ssStep: 1
    }

    Datepicker.EVENTS = {
        afterChange: 'afterchange.bjui.datepicker'
    }

    Datepicker.prototype.TOOLS = function () {
        var that = this
        var tools = {
            changeTmMenu: function (sltClass) {
                $tm.removeClass('hh').removeClass('mm').removeClass('ss')
                if (sltClass) {
                    $tm.addClass(sltClass)
                    $timeinps.removeClass('slt').filter('.' + sltClass).addClass('slt')
                }
            },
            clickTmMenu: function ($input, type) {
                $tm
                    .find('> ul')
                    .hide()
                    .filter('.' + type)
                    .show()
                    .find('> li')
                    .off(that.events.click_tm)
                    .on(that.events.click_tm, function () {
                        var $li = $(this)
                        var val = parseInt($li.text()) < 10 ? ('0' + $li.text()) : $li.text()

                        $input.val(val)
                    })
            },
            keydownInt: function (e) {
                if (!((e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode == BJUI.keyCode.DELETE || e.keyCode == BJUI.keyCode.BACKSPACE))) { return false }
            },
            changeTm: function ($input, $btn) {
                var ivalue = parseInt($input.val()), istart = parseInt($input.data('start')) || 0, iend = parseInt($input.data('end'))
                var istep = parseInt($input.data('step') || 1)
                var type = $btn ? ($btn.data('add') ? $btn.data('add') : -1) : 0
                var newVal = ivalue

                if (type == 1) {
                    if (ivalue <= iend - istep)
                        newVal = ivalue + istep
                } else if (type == -1) {
                    if (ivalue >= (istart + istep))
                        newVal = ivalue - istep
                } else if (ivalue > iend) {
                    newVal = iend
                } else if (ivalue < istart) {
                    newVal = istart
                }
                if (newVal < 10) newVal = '0' + newVal
                $input.val(newVal)
            },
            closeCalendar: function (flag) {
                tools.changeTmMenu()
                if (flag) {
                    $(document).off(that.events.click_close)
                    $box.hide()
                }
            },
            get: function (name) {
                return that.options[name]
            },
            getDays: function (y, m) {
                return m == 2 ? (y % 4 || (!(y % 100) && y % 400) ? 28 : 29) : (/4|6|9|11/.test(m) ? 30 : 31)
            },
            minMaxDate: function (sDate) {
                var _count = sDate.split('-').length - 1
                var _format = 'y-M-d'

                if (_count == 1) _format = 'y-M'
                else if (!_count) _format = 'y'

                return sDate.parseDate(_format)
            },
            getMinDate: function () {
                return this.minMaxDate(that.options.minDate)
            },
            getMaxDate: function () {
                var _sDate = that.options.maxDate
                var _count = _sDate.split('-').length - 1
                var _date = this.minMaxDate(_sDate)

                if (_count < 2) { //format:y-M、y
                    var _day = this.getDays(_date.getFullYear(), _date.getMonth() + 1)
                    _date.setDate(_day)
                    if (!_count)//format:y
                        _date.setMonth(11)
                }

                return _date
            },
            getDateWrap: function (date) {
                if (!date) date = this.parseDate(that.sDate) || new Date()

                var y = date.getFullYear(),
                    m = date.getMonth() + 1,
                    days = this.getDays(y, m)

                return {
                    year: y, month: m, day: date.getDate(),
                    hour: date.getHours(), minute: date.getMinutes(), second: date.getSeconds(),
                    days: days, date: date
                }
            },
            changeDate: function (y, m, d) {
                var dw = this.getDateWrap(), d = d || 1, date

                date = new Date(y, m - 1, d)

                if (date.getMonth() + 1 != m) {
                    date = new Date(y, date.getMonth(), 0)
                    date.setMonth(m - 1)
                }
                that.sDate = this.formatDate(date)

                return date
            },
            changeDateTime: function (y, M, d, H, m, s) {
                var date = new Date(y, M - 1, d, H, m, s)

                if (date.getMonth() + 1 != M) {
                    date = new Date(y, date.getMonth(), 0, H, m, s)
                    date.setMonth(M - 1)
                }
                that.sDate = this.formatDate(date)
                return date
            },
            changeDay: function (day, chMonth) {
                if (!chMonth) chMonth = 0
                var dw = this.getDateWrap()

                return this.changeDate(dw.year, dw.month + parseInt(chMonth), day)
            },
            changeMonth: function (type) {
                var yearIndex = $year.get(0).selectedIndex,
                    maxYear = $year.find('option').length,
                    month = ($month.val() * 1) + type

                if (!month) {
                    if (!yearIndex) {
                        month = 1
                    } else {
                        month = 12
                        yearIndex--
                        $year.get(0).selectedIndex = yearIndex
                    }
                } else if (month == 13) {
                    if (yearIndex == (maxYear - 1)) {
                        month = 12
                    } else {
                        month = 1
                        yearIndex++
                        $year.get(0).selectedIndex = yearIndex
                    }
                }
                $month.val(month).change()
            },
            parseDate: function (sDate) {
                if (!sDate) return null
                return sDate.parseDate(that.options.pattern)
            },
            formatDate: function (date) {
                return date.formatDate(that.options.pattern)
            },
            hasHour: function () {
                return that.options.pattern.indexOf('H') != -1
            },
            hasMinute: function () {
                return that.options.pattern.indexOf('m') != -1
            },
            hasSecond: function () {
                return that.options.pattern.indexOf('s') != -1
            },
            hasTime: function () {
                return this.hasHour() || this.hasMinute() || this.hasSecond()
            },
            hasDate: function () {
                var _dateKeys = ['y', 'M', 'd', 'E']

                for (var i = 0; i < _dateKeys.length; i++) {
                    if (that.options.pattern.indexOf(_dateKeys[i]) != -1) return true
                }
                return false
            },
            afterChange: function (date) {
                var value = date ? this.formatDate(date) : ''

                that.$element
                    .trigger(Datepicker.EVENTS.afterChange, { date: date, value: value })
                    .trigger('change')
            }
        }
        return tools
    }

    Datepicker.prototype.addBtn = function () {
        var that = this, $element = that.$element, fluid = $element.attr('size') ? '' : ' fluid'

        if ($element.data('nobtn')) return

        if (!this.$dateBtn && !this.options.addbtn && !$element.parent().hasClass('wrap_bjui_btn_box')) {
            this.$dateBtn = $(FRAG.dateBtn)
            this.$element.css({ 'paddingRight': '15px' }).wrap('<span class="wrap_bjui_btn_box' + fluid + '"></span>')

            var $box = this.$element.parent()
            var height = this.$element.addClass('form-control').innerHeight()

            $box.css({ 'position': 'relative', 'display': 'inline-block' })

            this.$dateBtn.css({ 'height': height, 'lineHeight': height + 'px' }).appendTo($box)
            this.$dateBtn.on('selectstart', function () { return false })
        }
    }

    Datepicker.prototype.init = function () {
        if (!this.$element.is(':text')) return
        if (this.$element.val()) this.sDate = this.$element.val().trim()

        var that = this,
            options = this.options,
            tools = this.tools,
            dw = tools.getDateWrap(),
            minDate = tools.getMinDate(), maxDate = tools.getMaxDate(),
            yearstart = minDate.getFullYear(), yearend = maxDate.getFullYear()

        if (!options.toggle || options.toggle !== 'datepicker' && !that.JSAPI)
            that.$element.data('JSAPI', true)

        $year.empty()
        for (var y = yearstart; y <= yearend; y++) {
            $year.append('<option value="' + y + '"' + (dw.year == y ? ' selected' : '') + '>' + y + '</option>')
        }

        $month.val(dw.month)
        $year.add($month).off(this.events.change_ym).on(this.events.change_ym, function () {
            if (tools.hasTime()) {
                var $day = $days.find('.slt'),
                    date = tools.changeDateTime($year.val(), $month.val(), $day.data('day'), dw.hour, dw.minute, dw.second)

                that.create(tools.getDateWrap(date), minDate, maxDate)
            } else {
                var $day = $days.find('.slt'),
                    date = tools.changeDate($year.val(), $month.val(), $day.data('day'))

                that.create(tools.getDateWrap(date), minDate, maxDate)
            }
        })
        $prev.off(this.events.click_prev).on(this.events.click_prev, function () {
            that.tools.changeMonth(-1)
        })
        $next.off(this.events.click_prev).on(this.events.click_prev, function () {
            that.tools.changeMonth(1)
        })
        $clearBtn.off(this.events.click_clear).on(this.events.click_clear, function () {
            that.$element.val('')
            tools.closeCalendar(true)
            tools.afterChange('')
        })
        $okBtn.off(this.events.click_ok).on(this.events.click_ok, function () {
            var $dd = $days.find('dd.slt')

            if ($dd.hasClass('disabled')) return false

            var date = tools.changeDay($dd.data('day'), $dd.data('month'))

            if (tools.hasTime()) {
                date.setHours(parseInt($hh.val()))
                date.setMinutes(parseInt($mm.val()))
                date.setSeconds(parseInt($ss.val()))
            }
            tools.closeCalendar(true)
            that.$element.val(tools.formatDate(date)).focus()

            //changedEvent
            tools.afterChange(date)
        })
        $close.off(this.events.click_close).on(this.events.click_close, function () {
            tools.closeCalendar(true)
        })
        $(document).off(this.events.click_close).on(this.events.click_close, function (e) {
            var $target = $(e.target)

            if (e.target == that.$element.get(0)) return
            if ($target.closest('#calendar').length) return
            if ($target.data('toggle') == 'datepicker' || $target.parent().data('toggle') == 'datepickerbtn' || $target.data('toggle') == 'datepickerbtn' || $target.data('JSAPI'))
                tools.closeCalendar(false)
            else
                tools.closeCalendar(true)
        })

        that.dw = dw
        that.minDate = minDate
        that.maxDate = maxDate

        // if js API
        if (that.$element.data('JSAPI')) {
            // add btn
            if (!options.nobtn)
                that.addBtn()
            // event
            that.$element.off('click.bjui.datepicker.data-api').on('click.bjui.datepicker.data-api', $.proxy(function (e) {
                that.create()
            }, that))

            if (!options.nobtn) {
                that.$element.parent('.wrap_bjui_btn_box').find('[data-toggle="datepickerbtn"]').off('click.bjui.datepicker.data-api').on('click.bjui.datepicker.data-api', $.proxy(function (e) {
                    that.create()
                }, that))
            }
        }
    }

    Datepicker.prototype.create = function (dw, minDate, maxDate) {
        if (!dw) {
            this.init()

            dw = this.dw
            minDate = this.minDate
            maxDate = this.maxDate
        }

        var that = this,
            options = this.options,
            tools = this.tools,
            monthStart = new Date(dw.year, dw.month - 1, 1),
            startDay = monthStart.getDay(),
            dayStr = ''

        if (startDay > 0) {
            monthStart.setMonth(monthStart.getMonth() - 1)
            var prevDateWrap = tools.getDateWrap(monthStart)

            for (var t = prevDateWrap.days - startDay + 1; t <= prevDateWrap.days; t++) {
                var _date = new Date(dw.year, dw.month - 2, t),
                    _ctrClass = (_date >= minDate && _date <= maxDate) ? '' : ' disabled'

                dayStr += '<dd class="other' + _ctrClass + '" data-month="-1" data-day="' + t + '">' + t + '</dd>'
            }
        }
        for (var t = 1; t <= dw.days; t++) {
            var _date = new Date(dw.year, dw.month - 1, t),
                _ctrClass = (_date >= minDate && _date <= maxDate) ? '' : 'disabled'

            if (t == dw.day)
                _ctrClass += ' slt'
            dayStr += '<dd class="' + _ctrClass + '" data-day="' + t + '">' + t + '</dd>'
        }
        for (var t = 1; t <= 42 - startDay - dw.days; t++) {
            var _date = new Date(dw.year, dw.month, t),
                _ctrClass = (_date >= minDate && _date <= maxDate) ? '' : ' disabled'

            dayStr += '<dd class="other' + _ctrClass + '" data-month="1" data-day="' + t + '">' + t + '</dd>'
        }

        var $alldays = $days.html(dayStr).find('dd')

        $alldays.not('.disabled').off(this.events.click_days).on(this.events.click_days, function () {
            var $day = $(this)

            if (!tools.hasTime()) {
                var date = tools.changeDay($day.data('day'), $day.data('month'))

                tools.closeCalendar(true)
                that.$element.val(tools.formatDate(date)).focus()

                //changedEvent
                tools.afterChange(date)
            } else {
                $alldays.removeClass('slt')
                $day.addClass('slt')
            }
        })

        if (!tools.hasDate()) {
            $main.addClass('nodate') // only time
        } else {
            $main.removeClass('nodate')
        }
        if (tools.hasTime()) {
            $time.show()
            $hh.val(dw.hour < 10 ? ('0' + dw.hour) : dw.hour).off(this.events.focus_time).on(this.events.focus_time, function () {
                tools.changeTmMenu('hh')
            })

            var iMinute = parseInt(dw.minute / options.mmStep) * options.mmStep

            $mm.val(iMinute < 10 ? ('0' + iMinute) : iMinute).data('step', options.mmStep).off(this.events.focus_time).on(this.events.focus_time, function () {
                tools.changeTmMenu('mm')
            })
            $ss.val(tools.hasSecond() ? (dw.second < 10 ? ('0' + dw.second) : dw.second) : '00').data('step', options.ssStep).off(this.events.focus_time).on(this.events.focus_time, function () {
                tools.changeTmMenu('ss')
            })
            $box.off('click').on('click', function (e) {
                if ($(e.target).closest('.time').length) return
                $tm.find('> ul').hide()
                tools.changeTmMenu()
            })
            $timeinps.off(this.events.keydown_time).on(this.events.keydown_time, tools.keydownInt).each(function () {
                var $input = $(this)

                $input.off(that.events.keyup_time).on(that.events.keyup_time, function () {
                    tools.changeTm($input)
                })
            }).off(this.events.click_time).on(this.events.click_time, function () {
                tools.clickTmMenu($(this), $(this).data('type'))
            })

            var timer = null

            $spinner.off(this.events.click_spinner).on(this.events.click_spinner, function (e) {
                var $btn = $(this)

                $timeinps.filter('.slt').each(function () {
                    tools.changeTm($(this), $btn)
                })

                e.preventDefault()
            }).off(this.events.mousedown_sp).on(this.events.mousedown_sp, function (e) {
                var $btn = $(this)

                timer = setInterval(function () {
                    $timeinps.filter('.slt').each(function () {
                        tools.changeTm($(this), $btn)
                    })
                }, 150)
            }).off(this.events.mouseup_sp).on(this.events.mouseup_sp, function (e) {
                clearTimeout(timer)
            })

            $hh.prop('disabled', !tools.hasHour())
            $mm.prop('disabled', !tools.hasMinute())
            $ss.prop('disabled', !tools.hasSecond())
        } else {
            $time.hide()
        }
        this.show()
    }

    Datepicker.prototype.show = function () {
        var offset = this.$element.offset(),
            iTop = offset.top + this.$element.get(0).offsetHeight,
            iLeft = offset.left,
            iBoxH = $box.outerHeight(true),// fix top
            iBoxW = $box.outerWidth(true), // fix left
            css = {}

        if (iTop > iBoxH && iTop > $(window).height() - iBoxH)
            iTop = offset.top - iBoxH

        css.left = iLeft
        css.top = iTop

        if (iLeft > $(window).width() - iBoxW) {
            $box.css('left', '')

            delete css.left
            css.right = 10
        }

        $box.css(css).show().click(function (e) {
            e.stopPropagation()
        })
    }

    // DATEPICKER PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments
        var property = option

        return this.each(function () {
            var $this = $(this)
            var options = $.extend({}, Datepicker.DEFAULTS, $this.data(), typeof option === 'object' && option)
            var data = $this.data('bjui.datepicker')

            if (!data) $this.data('bjui.datepicker', (data = new Datepicker(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.datepicker

    $.fn.datepicker = Plugin
    $.fn.datepicker.Constructor = Datepicker

    // DATEPICKER NO CONFLICT
    // =================

    $.fn.datepicker.noConflict = function () {
        $.fn.datepicker = old
        return this
    }

    // DATEPICKER DATA-API
    // ==============

    $(document).on(BJUI.eventType.initUI, function (e) {
        $(e.target).find('[data-toggle="datepicker"]').each(function () {
            var $this = $(this)

            if (!$this.length) return
            if ($this.data('nobtn')) return

            Plugin.call($this, 'addBtn')
        })
    })

    $(document).on('click.bjui.datepicker.data-api', '[data-toggle="datepickerbtn"]', function (e) {
        var $date = $(this).prevAll('[data-toggle="datepicker"]')

        if (!$date || !$date.is(':text')) return
        Plugin.call($date, 'create')

        e.preventDefault()
    })

    $(document).on('click.bjui.datepicker.data-api', '[data-toggle="datepicker"]', function (e) {
        var $this = $(this)

        if ($this.data('onlybtn')) return
        if (!$this.is(':text')) return
        Plugin.call($this, 'create')

        e.preventDefault()
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-ajaxtab.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-ajaxtab.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // AJAXTAB CLASS DEFINITION
    // ======================

    var Ajaxtab = function (element, options) {
        this.$element = $(element)
        this.options = options
    }

    Ajaxtab.DEFAULTS = {
        url: undefined,
        target: undefined,
        reload: false
    }

    Ajaxtab.prototype.init = function () {
        var options = this.options

        if (!(options.url)) {
            BJUI.debug('Ajaxtab Plugin: Error trying to open a tab, url is undefined!')
            return
        } else {
            options.url = decodeURI(options.url).replacePlh(this.$element.closest('.unitBox'))

            if (!options.url.isFinishedTm()) {
                this.$element.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('Ajaxtab Plugin: The new ajaxtab\'s url is incorrect, url: ' + options.url)
                return
            }

            options.url = encodeURI(options.url)
        }
        if (!options.target) {
            BJUI.debug('Ajaxtab Plugin: Attribute \'target\' is not defined!')
            return
        }
        if (options.reload) {
            this.load()
        } else {
            var reload = this.$element.data('bjui.ajaxtab.reload')

            if (!reload) this.load()
            else this.$element.tab('show')
        }
    }

    Ajaxtab.prototype.load = function () {
        var $element = this.$element
        var options = this.options

        $(options.target).ajaxUrl({
            url: options.url,
            data: {},
            callback: function () {
                $element.data('bjui.ajaxtab.reload', true).tab('show')
            }
        })
    }

    // AJAXTAB PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments
        var property = option

        return this.each(function () {
            var $this = $(this)
            var options = $.extend({}, Ajaxtab.DEFAULTS, $this.data(), typeof option === 'object' && option)
            var data = $this.data('bjui.ajaxtab')

            if (!data) $this.data('bjui.ajaxtab', (data = new Ajaxtab(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.ajaxtab

    $.fn.ajaxtab = Plugin
    $.fn.ajaxtab.Constructor = Ajaxtab

    // AJAXTAB NO CONFLICT
    // =================

    $.fn.ajaxtab.noConflict = function () {
        $.fn.ajaxtab = old
        return this
    }

    // AJAXTAB DATA-API
    // ==============

    $(document).on('click.bjui.ajaxtab.data-api', '[data-toggle="ajaxtab"]', function (e) {
        var $this = $(this)
        var options = $this.data()

        if (!options.url) options.url = $this.attr('href')
        Plugin.call($this, options)

        e.preventDefault()
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-datagrid.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-datagrid.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // DATAGRID CLASS DEFINITION
    // ======================

    var Datagrid = function (element, options) {
        this.$element = $(element)
        this.options = options
        this.tools = this.TOOLS()

        this.datanames = {
            tbody: 'bjui.datagrid.tbody.dom',
            td_html: 'bjui.datagrid.td.html',
            changeData: 'bjui.datagrid.tr.changeData'
        }

        this.classnames = {
            s_checkbox: 'datagrid-checkbox',
            s_linenumber: 'datagrid-linenumber',
            s_edit: 'datagrid-column-edit',
            s_lock: 'datagrid-lock',
            s_menu: 'datagrid-menu-box',
            s_filter: 'datagrid-filter-box',
            s_showhide: 'datagrid-showhide-box',
            th_cell: 'datagrid-cell',
            th_menu: 'datagrid-column-menu',
            btn_menu: 'datagrid-column-menu-btn',
            th_col: 'datagrid-col',
            th_field: 'datagrid-col-field',
            th_sort: 'datagrid-sortable',
            th_resize: 'datagrid-resize-head',
            th_resizemark: 'datagrid-column-resizemark',
            tr_child: 'datagrid-child-tr',
            tr_edit: 'datagrid-edit-tr',
            tr_add: 'datagrid-add-tr',
            tr_selected: 'datagrid-selected-tr',
            td_edit: 'datagrid-edit-td',
            td_changed: 'datagrid-changed',
            td_child: 'datagrid-child-td',
            td_linenumber: 'datagrid-linenumber-td',
            td_checkbox: 'datagrid-checkbox-td',
            li_asc: 'datagrid-li-asc',
            li_desc: 'datagrid-li-desc',
            li_filter: 'datagrid-li-filter',
            li_showhide: 'datagrid-li-showhide',
            li_lock: 'datagrid-li-lock',
            li_unlock: 'datagrid-li-unlock'
        }
    }

    Datagrid.DEFAULTS = {
        gridTitle: '',
        columns: null,     // Thead column module
        data: null,     // Data source
        dataUrl: null,     // Request data source URL, for processing (filtering / sorting / paging) results
        updateRowUrl: null,     // Update row data URL, return JSON data
        postData: null,     // Send other data to server
        loadType: 'POST',   // Ajax load request type
        dataType: 'json',   // Data type of data source
        local: 'remote', // Optional 'local' | 'remote'
        fieldSortable: true,     // Click the field to sort
        filterThead: true,     // Filter in the thead
        sortAll: true,     // Sort scope, false = this page, true = all
        filterAll: true,     // Filter scope, false = this page, true = all
        filterMult: true,     // Filter multiple fileds, false = single, true = multiple
        initFilter: {},       // 
        selectMult: false,    // When clicked tr, multiple selected (Default need to press Ctrl or Shift or used checkbox)
        linenumberAll: false,    // All data together numbers
        showLinenumber: true,     // Display linenumber column, Optional 'true' | 'false' | 'lock', (Optional 'true, false' is a boolean)
        showCheckboxcol: false,    // Display checkbox column, Optional 'true' | 'false' | 'lock', (Optional 'true, false' is a boolean)
        showChildcol: undefined,// Display child button column, Optional 'true' | 'false' | 'lock', (Optional 'true, false' is a boolean). If not set false and 'hasChild == true', showChildcol = true
        showEditbtnscol: false,    // Display edit buttons column, Optional 'true' | 'false' | 'edit', (Optional 'true, false' is a boolean, Optional 'edit' is custom column label)
        customEditbtns: {         // Custom edit column buttons
            position: 'after',  // Button inserted position, Optional 'after' | 'before' | 'replace'
            buttons: null,     // Custom buttons, Set string or function
            width: 0         // The edit column width, 0 = 'auto'
        },
        showTfoot: false,    // Display the tfoot, Optional 'true' | 'false' | 'lock', (Optional 'true, false' is a boolean)
        showToolbar: false,    // Display datagrid toolbar
        showNoDataTip: true,     // Display 'no data' tips, Optional 'true' | 'false' | 'string', (Optional 'true, false' is a boolean, Optional 'string' is custom tips)
        toolbarItem: '',       // Displayed on the toolbar elements, Optional 'all, add, edit, cancel, save, del, import, export, exportf |'
        toolbarCustom: '',       // Html code || function || jQuery dom object, custom elements, displayed on the toolbar
        columnResize: true,     // Allow adjust the column width
        columnMenu: true,     // Display the menu button on the column
        columnShowhide: true,     // On the menu display (show / hide columns)
        columnFilter: false,    // On the menu display (filter form)
        columnLock: true,     // On the menu display (lock / unlock columns)
        paging: true,     // Display pagination component
        pagingAlign: 'center', // The pagination component alignment
        keys: {
            gridChild: 'gridChild',
            gridNumber: 'gridNumber',
            gridCheckbox: 'gridCheckbox',
            gridEdit: 'gridEdit',
            gridIndex: 'gridIndex',
            dropFlag: 'dropFlag',
            treePTr: 'datagrid.tree.parentTr',
            treePData: 'datagrid.tree.parentData',
            childlen: 'childlen',
            isExpand: 'isExpand',
            isParent: 'isparent'
        },
        hasChild: false,    // It contains child data, Optional (true | false)
        childOptions: {         // Child grid options
            width: '100%',
            height: 'auto',
            paging: false,
            columnMenu: false,
            filterThead: false,
            childUpdate: false     // This options only for childOptions, when the child grid data changes, update the parent row(!! The parent grid neeed option 'updateRowUrl' !!), Optional (Boolean: true | false, true = 'all') OR (String: 'all, add, edit, del')
        },
        isTree: false,
        treeOptions: {
            keys: {
                key: 'id',
                parentKey: 'pid',
                childKey: 'children',
                childLen: 'childlen',
                isParent: 'isparent',
                level: 'level',
                order: 'order',
                isExpand: 'isExpand'
            },
            simpleData: true,
            expandAll: false,
            add: true
        },
        dropOptions: {
            drop: false,
            dropUrl: null,
            paging: true,     // send paging data
            scope: 'drop',   // send data scope, Optional 'drop' | 'all'
            beforeDrag: null,     // Function - before drag, return true can drag
            beforeDrop: null,     // Function - before drop, return false cancel drop
            afterDrop: 'POST'    // Post(Ajax) the current page data to server, request type = editType. Optional ('POST' | function), If set a function, doesn't post
        },
        tdTemplate: '',
        editUrl: null,     // An action URL, for processing (update / save), return results (json)
        editCallback: null,     // Callback for save
        editMode: 'inline', // Editing mode, Optional 'false' | 'inline' | 'dialog', (Optional 'false' is a boolean)
        editDialogOp: null,     // For dialog edit, the dialog init options
        editType: 'POST',   // Ajax request method of edit, Optional 'POST' | 'GET' | 'raw', (Optional 'raw' is post raw data)
        inlineEditMult: true,     // Can inline edit multiple rows
        saveAll: true,     // For inline edit, true = save current row, false = save all editing rows
        addLocation: 'first',  // Add rows to datagrid location, Optional 'first' | 'last' | 'prev' | 'next'
        delUrl: null,     // The delete URL, return delete tr results (json)
        delType: 'POST',   // Delete URL of ajax request method
        delPK: null,     // Ajax delete request to send only the primary key
        delConfirm: true,     // Delete confirmation message, Optional 'true' | 'false' | 'message', (Optional 'true, false' is a boolean)
        delCallback: null,     // Callback for delete
        jsonPrefix: '',       // JSON object key prefix, for post data
        contextMenuH: true,     // Right-click on the thead, display the context menu
        contextMenuB: false,    // Right-click on the tbody tr, display the context menu
        templateWidth: 600,      // 
        flowLayoutWidth: 0,        // 
        dialogFilterW: 360,      // 
        hScrollbar: false,    // Allowed horizontal scroll bar
        fullGrid: false,    // If the table width below gridbox width, stretching table width
        importOption: null,     // Import btn options
        exportOption: null,     // Export btn options
        beforeEdit: null,     // Function - before edit method, return true execute edit method
        beforeDelete: null,     // Function - before delete method, return true execute delete method
        beforeSave: null,     // Function - before save method, arguments($trs, datas)
        afterSave: null,     // Function - after save method, arguments($trs, datas)
        afterDelete: null      // Function - after delete method
    }

    Datagrid.renderItem = function (value, data, items, itemattr) {
        if (!items || !items.length) return ''
        var label = ''

        $.each(items, function (i, n) {
            if (itemattr && itemattr.value) {
                if (n[itemattr.value] == value) {
                    label = n[itemattr.label]
                    return false
                }
            } else {
                if (typeof n[value] !== 'undefined') {
                    label = n[value]
                    return false
                }
            }
        })

        return label
    }

    Datagrid.renderItemMulti = function (value, data, items, itemattr) {
        if (!items || !items.length) return ''
        var label = [], val = $.type(value) === 'array' ? value : String(value).split(',')

        $.each(items, function (i, n) {
            $.each(val, function (k, v) {
                if (itemattr && itemattr.value) {
                    if (n[itemattr.value] == v) {
                        label.push(n[itemattr.label])
                        return false
                    }
                } else {
                    if (typeof n[v] !== 'undefined') {
                        label.push(n[v])
                        return false
                    }
                }
            })
        })

        return label.join(',')
    }

    Datagrid.prototype.TOOLS = function () {
        var that = this, options = that.options
        var tools = {
            getPageCount: function (pageSize, total) {
                return Math.ceil(total / pageSize)
            },
            getPageInterval: function (count, pageCurrent, showPageNum) {
                var half = Math.ceil(showPageNum / 2), limit = count - showPageNum,
                    start = pageCurrent > half ? Math.max(Math.min(pageCurrent - half, limit), 0) : 0,
                    end = pageCurrent > half ? Math.min((pageCurrent + half), count) : Math.min(showPageNum, count)

                if (end - start == showPageNum) end = end + 1
                if (end < showPageNum) end = end + 1
                if (start + 1 == end) end = end + 1

                return { start: start + 1, end: end }
            },
            getRight: function ($obj) {
                var width = 0, index = $obj.data('index'), model = that.columnModel

                for (var i = index; i >= 0; i--) {
                    width += model[i].th.outerWidth()
                }

                return width
            },
            getRight4Lock: function (index) {
                var width = 0, $td = that.$lockTbody.find('> tr:first > td:eq(' + index + ')'), $firstTds = $td && $td.prevAll().add($td)

                if (!$firstTds || !$firstTds.length) $firstTds = that.$lockColgroupH.filter(':lt(' + (index + 1) + ')')
                $firstTds.each(function () {
                    var $td = $(this), w = $td.is(':hidden') ? 0 : $td.outerWidth()

                    width += w
                })

                return width
            },
            beforeEdit: function ($trs, datas) {
                var beforeEdit = options.beforeEdit

                if (beforeEdit) {
                    if (typeof beforeEdit === 'string') beforeEdit = beforeEdit.toFunc()
                    if (typeof beforeEdit === 'function') {
                        return beforeEdit.call(that, $trs, datas)
                    }
                }

                return true
            },
            beforeSave: function ($trs, data) {
                var beforeSave = options.beforeSave

                if (beforeSave) {
                    if (typeof beforeSave === 'string') beforeSave = beforeSave.toFunc()
                    if (typeof beforeSave === 'function') {
                        return beforeSave.call(that, $trs, data)
                    }
                }

                return true
            },
            afterSave: function ($trs, data) {
                var afterSave = options.afterSave, childUpdate = that.options.childUpdate, $parent = that.$element.data('bjui.datagrid.parent'),
                    updateParent = function ($tr) {
                        $tr.closest('table').datagrid('updateRow', $tr)
                    }

                if (afterSave) {
                    if (typeof afterSave === 'string') afterSave = afterSave.toFunc()
                    if (typeof afterSave === 'function') {
                        afterSave.call(that, $trs, data)
                    }
                }

                // remove add data
                $trs.each(function () {
                    $(this).removeData('datagrid.addData')
                })

                // update child parent
                if ($parent && childUpdate) {
                    if (typeof childUpdate === 'string' && childUpdate.indexOf('all') === -1) {
                        if (data.addFlag && childUpdate.indexOf('add') !== -1)
                            updateParent($parent)
                        else if (childUpdate.indexOf('edit') !== -1)
                            updateParent($parent)
                    } else {
                        updateParent($parent)
                    }
                }

                if (that.needfixedWidth) {
                    that.fixedWidth()
                    that.needfixedWidth = false
                }

                // fixedH
                if (options.height === 'auto') {
                    var scrollTop = that.$boxB.scrollTop()

                    that.$boxB.scrollTop(5)
                    if (that.$boxB.scrollTop()) {
                        that.fixedHeight()
                        that.$boxB.scrollTop(scrollTop)
                    }
                }

                that.$element.data('allData', that.allData)
            },
            afterDelete: function () {
                var afterDelete = options.afterDelete

                if (afterDelete) {
                    if (typeof afterDelete === 'string') afterDelete = afterDelete.toFunc()
                    if (typeof afterDelete === 'function') {
                        afterDelete.call(that)
                    }
                }

                that.$element.data('allData', that.allData)
            },
            // Correct colspan
            setColspan: function (column, colspanNum) {
                if (column.colspan) column.colspan = column.colspan + colspanNum - 1
                column.index = column.index + colspanNum - 1
                if (column.parent) this.setColspan(column.parent, colspanNum)
            },
            // set columns options
            setOp: function (op) {
                if (!op.name) {
                    op.menu = op.lock = op.edit = op.add = op.quickSort = op.quickfilter = false
                } else {
                    op.menu = (typeof op.menu === 'undefined') ? true : op.menu
                    op.lock = (typeof op.lock === 'undefined') ? true : op.lock
                    op.edit = (typeof op.edit === 'undefined') ? true : op.edit
                    op.add = (typeof op.add === 'undefined') ? true : op.add
                    op.quicksort = (typeof op.quicksort === 'undefined') ? true : op.quicksort
                    op.quickfilter = (typeof op.quickfilter === 'undefined') ? true : op.quickfilter
                    op.finalWidth = (typeof op.finalWidth === 'undefined') ? false : op.finalWidth
                }
                op.hide = (typeof op.hide === 'undefined') ? false : op.hide

                return op
            },
            json2Array4Tree: function (options, datas, level, pid) {
                if (!datas) return []
                if (!level) level = 0

                var k = options.key, pk = options.parentKey, childKey = options.childKey, childLen = options.childLen, levelKey = options.level, isParent = options.isParent, r = [], data

                if (datas) {
                    datas.sort(function (a, b) {
                        return a[options.order] - b[options.order]
                    })
                }

                if ($.isArray(datas)) {
                    for (var i = 0, l = datas.length; i < l; i++) {
                        data = $.extend({}, datas[i])

                        data[levelKey] = level
                        r.push(data)

                        if (data[childKey]) {
                            delete data[childKey]
                            data[isParent] = true
                            data[childLen] = datas[i][childKey].length
                            pid = data[k]

                            r = r.concat(this.json2Array4Tree(options, datas[i][childKey], data[levelKey] + 1, pid))

                            pid = null
                        }
                    }
                } else {
                    data = $.extend({}, datas)

                    data[levelKey] = level
                    r.push(data)

                    if (data[childKey]) {
                        delete data[childKey]
                        data[isParent] = true
                        data[childLen] = datas[childKey].length
                        pid = data[k]

                        r = r.concat(this.json2Array4Tree(options, datas[childKey], data[levelKey] + 1))

                        pid = null
                    }
                }

                return r
            },
            array2Json4Tree: function (options, datas) {
                var r = [], temp = [], keys = options.keys, key = keys.key, parentKey = keys.parentKey, childKey = keys.childKey

                if (!$.isArray(datas)) return [datas]

                for (var i = 0, l = datas.length; i < l; i++) {
                    temp[datas[i][key]] = datas[i]
                }
                for (var i = 0, l = datas.length; i < l; i++) {
                    if (datas[i][parentKey] && datas[i][key] !== datas[i][parentKey]) {
                        if (!temp[datas[i][parentKey]])
                            temp[datas[i][parentKey]] = []
                        if (!temp[datas[i][parentKey]][childKey])
                            temp[datas[i][parentKey]][childKey] = []

                        temp[datas[i][parentKey]][childKey].push(datas[i])
                    } else {
                        r.push(datas[i])
                    }
                }

                return r
            },
            // create trs by data source
            createTrsByData: function (data, refreshFlag) {
                var list

                if (!that.$tbody) that.$tbody = $('<tbody></tbody>')
                if (data) {
                    if (data.list) list = data.list
                    else list = data

                    that.paging.total = list.length || 0

                    // for tree
                    if (that.options.isTree) {
                        if (that.options.treeOptions.simpleData) {
                            list = this.array2Json4Tree(that.options.treeOptions, list)
                            list = this.json2Array4Tree(that.options.treeOptions.keys, list)
                        } else {
                            list = this.json2Array4Tree(that.options.treeOptions.keys, list)
                        }
                    }

                    if (list && !$.isArray(list))
                        list = [list]

                    that.paging.total = (list && list.length) || 0

                    if (!that.paging.total)
                        list = []

                    if (typeof data === 'object' && that.paging.total) {
                        if (data[BJUI.pageInfo.total]) that.paging.total = parseInt(data[BJUI.pageInfo.total], 10)
                        if (data[BJUI.pageInfo.pageSize]) {
                            if (refreshFlag && that.paging.pageSize != data[BJUI.pageInfo.pageSize]) {
                                that.$boxP && that.$boxP.trigger('bjui.datagrid.paging.pageSize', data[BJUI.pageInfo.pageSize])
                            }
                            that.paging.pageSize = parseInt(data[BJUI.pageInfo.pageSize], 10)
                        }
                    }

                    that.paging.pageCount = tools.getPageCount(that.paging.pageSize, that.paging.total)

                    if (that.paging.pageCurrent > that.paging.pageCount) that.paging.pageCurrent = that.paging.pageCount
                    if (!that.paging.pageCurrent) that.paging.pageCurrent = 1

                    this.initTbody(list, refreshFlag)
                }

                if (!that.init_tbody) that.$tbody.appendTo(that.$tableB)
                if (!that.init_thead) that.initThead()
            },
            // initTbody
            initTbody: function (data, refreshFlag) {
                var tools = this, allData = that.allData, type = options.dataType || 'json', model = that.columnModel, hiddenFields = that.hiddenFields, regional = that.regional, newData = [], attach = that.attach, json
                var paging = that.paging, start = 0, end = paging.pageSize, keys = that.options.keys
                var doInit = function () {
                    type = type.toLowerCase()
                    if (data) allData = that.allData = data

                    that.$element.data('allData', that.allData)

                    if (!allData.length) {
                        end = 0
                    } else {
                        if (options.local === 'local') {
                            start = (paging.pageSize * (paging.pageCurrent - 1))
                            end = start + paging.pageSize
                            if (paging.total != allData.length) paging.total = allData.length
                            if (start > allData.length) start = paging.pageSize * (paging.pageCount - 1)
                        } else {
                            if (allData.length > paging.pageSize) end = paging.pageSize
                        }
                    }
                    if (end > allData.length) end = allData.length

                    // array to json
                    if (type === 'array' && data && data.length && $.type(data[0]) === 'array') {
                        var a1 = start, a2 = end, arrData = [], _index

                        if (options.local === 'local') {
                            a1 = 0
                            a2 = allData.length
                        }
                        for (var i = a1; i < a2; i++) {
                            json = {}
                            _index = 0
                            $.each(allData[i], function (k, v) {
                                var obj, val = v

                                if (model[_index] && model[_index][keys.gridChild]) _index++
                                if (model[_index] && model[_index][keys.gridNumber]) _index++
                                if (model[_index] && model[_index][keys.gridCheckbox]) _index++
                                if (typeof val === 'string') val = '"' + val + '"'

                                if (model[_index] && !model[_index][keys.gridEdit]) {
                                    obj = '{"' + model[_index].name + '":' + val + '}'
                                    $.extend(json, JSON.parse(obj))
                                } else { // init hidden fields
                                    if (model[_index] && model[_index][keys.gridEdit]) _index++
                                    if (_index >= model.length && hiddenFields) {
                                        if (hiddenFields[k - model.length]) {
                                            obj = '{"' + hiddenFields[k - model.length] + '":' + val + '}'
                                            $.extend(json, JSON.parse(obj))
                                        }
                                    }
                                }
                                _index++
                            })

                            arrData.push(json)
                        }

                        allData = that.allData = arrData
                    }

                    // create cuttent page data
                    for (var i = start; i < end; i++) {
                        json = $.extend({}, that.allData[i], attach)

                        /* for quickSort */
                        if (options.local === 'local' && !refreshFlag) {
                            json['bjui_local_index'] = i
                            that.allData[i]['bjui_local_index'] = i
                        }

                        newData.push(json)
                    }

                    tools.createTrs(newData, refreshFlag)

                    that.data = newData

                    that.$element.trigger('afterLoad.bjui.datagrid', { datas: newData })
                }

                if (refreshFlag && that.$boxM) {
                    that.$boxM.show().trigger('bjui.ajaxStop').trigger('bjui.ajaxStart', [50, doInit])
                } else {
                    doInit()
                }
            },
            // create tbody - tr
            createTrs: function (datas, refreshFlag) {
                var tools = this, keys = that.options.keys, model = that.columnModel, paging = that.paging, trs = [], editFrag = BJUI.doRegional(FRAG.gridEditBtn, that.regional), childFrag = BJUI.doRegional(FRAG.gridExpandBtn, that.regional), lockedCols = [], isRenderOnly = false,
                    customEditbtns = that.options.customEditbtns, editFrag2 = customEditbtns.buttons

                // custom edit column buttons
                if (editFrag2) {
                    if (typeof editFrag2 === 'function')
                        editFrag2 = editFrag2.apply()

                    if (editFrag2) {
                        if (customEditbtns.position === 'before')
                            editFrag = editFrag2 + editFrag
                        else if (customEditbtns.position === 'replace')
                            editFrag = editFrag2
                        else
                            editFrag += editFrag2
                    }
                }

                if (refreshFlag) {
                    // remebered lock columns
                    $.each(model, function (i, n) {
                        if (n.locked) {
                            that.colLock(n.th, false)
                            n.lock_refresh = true
                        }
                    })

                    if (that.$tbody.find('> tr.datagrid-nodata').length) {
                        that.needfixedWidth = true
                    }

                    that.$tbody.empty()
                    that.$lockTableH && that.$lockTableH.empty()
                    that.$lockTableB && that.$lockTableB.empty()
                }

                if (!datas.length && !refreshFlag) {
                    var emptyObj = {}

                    emptyObj[model[0].name] = '0'
                    isRenderOnly = true
                    datas = []
                    datas.push(emptyObj)
                }

                for (var i = 0, l = datas.length; i < l; i++) {
                    var trData = datas[i], modellength = model.length, tempcolspan = modellength, linenumber = options.linenumberAll ? ((paging.pageCurrent - 1) * paging.pageSize + (i + 1)) : (i + 1),
                        tds = [], n, tdHtml = BJUI.StrBuilder(), $td, name, label, _label, render_label, align, cls, display, tree, hasTree = false, treeattr = '', tdTemplate = options.tdTemplate

                    trData[keys.gridNumber] = linenumber
                    trData[keys.gridIndex] = i

                    if (that.options.hasChild && that.options.childOptions)
                        tempcolspan = tempcolspan - 1
                    if (that.options.showLinenumber)
                        tempcolspan = tempcolspan - 1
                    if (that.options.showCheckboxcol)
                        tempcolspan = tempcolspan - 1

                    var tempData = $.extend({}, trData)

                    for (var j = 0; j < modellength; j++) {
                        n = model[j]
                        name = n.name || 'datagrid-noname'
                        label = trData[name]
                        align = ''
                        cls = []
                        _label = ''
                        display = ''
                        tree = ''
                        render_label = undefined

                        if (n.pattern && n.type === 'date') {
                            label = dateFmt(label, n.pattern);
                        }

                        if (typeof label === 'undefined' || label === 'null' || label === null) label = ''
                        _label = label

                        if (n.align) align = ' align="' + n.align + '"'
                        if (n[keys.gridChild]) label = childFrag
                        if (n[keys.gridCheckbox]) label = '<div><input type="checkbox" data-toggle="icheck" name="datagrid.checkbox" value="true"></div>'
                        if (n[keys.gridEdit]) {
                            label = editFrag
                            cls.push(that.classnames.s_edit)
                        }
                        if (options.hScrollbar) {
                            if (!n.width || n.width === 'auto')
                                cls.push('nowrap')
                        }

                        /* for tfoot */
                        /*if (n.calc) {
                            if (!n.calc_count) n.calc_count = datas.length
                            
                            var number = label ? (String(label).isNumber() ? Number(label) : 0) : 0
                            
                            if (n.calc === 'sum' || n.calc === 'avg') n.calc_sum = (n.calc_sum || 0) + number
                            else if (n.calc === 'max') n.calc_max = n.calc_max ? (n.calc_max < number ? number : n.calc_max) : number
                            else if (n.calc === 'min') n.calc_min = n.calc_min ? (n.calc_min > number ? number : n.calc_min) : number
                        }*/

                        if (n[keys.gridChild]) cls.push(that.classnames.td_child)
                        if (n[keys.gridNumber]) cls.push(that.classnames.td_linenumber)
                        if (n[keys.gridCheckbox]) cls.push(that.classnames.td_checkbox)

                        if (cls.length) cls = cls.join(' ')
                        else cls = ''

                        if (refreshFlag && n.hidden) display = ' style="display:none;"'

                        /* render */
                        if (n.items && !n.render) {
                            if (n.attrs && n.attrs['multiple'])
                                n.render = $.datagrid.renderItemMulti
                            else
                                n.render = $.datagrid.renderItem
                        }
                        if (n.render && typeof n.render === 'string') n.render = n.render.toFunc()
                        if (n.render && typeof n.render === 'function') {
                            if (n.items) {
                                if (typeof n.items === 'string') {
                                    if (n.items.trim().startsWith('[')) n.items = n.items.toObj()
                                    else n.items = n.items.toFunc()
                                }

                                if (!that.renderTds) that.renderTds = []

                                var delayRender = function (index, label, trData, n) {
                                    $.when(n.items.call(that)).done(function (item) {
                                        n.items = item

                                        that.delayRender--
                                    })
                                }

                                if (typeof n.items === 'function') {
                                    if (!i) {
                                        if (!that.delayRender) that.delayRender = 0
                                        that.delayRender++
                                        delayRender((i * j), _label, trData, n)
                                    }
                                    n.delayRender = true
                                    that.renderTds.push({ trindex: i, tdindex: j, label: _label, data: trData, render: n.render })
                                } else {
                                    delete n.delayRender
                                    render_label = n.render.call(that, _label, trData, n.items, n.itemattr)
                                }
                            } else {
                                render_label = n.render.call(that, _label, trData)
                            }
                        }

                        if (options.isTree && !hasTree && (!(n === that.childColumn || n === that.linenumberColumn || n === that.checkboxColumn || n === that.editBtnsColumn) || that.treeColumn)) {
                            if (typeof options.isTree === 'string') {
                                if (n.name && n.name === options.isTree)
                                    hasTree = true
                            } else {
                                hasTree = true
                            }

                            if (hasTree) {
                                !that.treeColumn && (that.treeColumn = n)
                                n.hasTree = true
                                !trData[options.treeOptions.keys.level] && (trData[options.treeOptions.keys.level] = 0)
                                tree = that.tools.createTreePlaceholder(trData, (render_label || label))
                                align = ' align="left"'

                                if (cls) {
                                    cls += ' datagrid-tree-td'
                                } else {
                                    cls = 'datagrid-tree-td'
                                }

                                treeattr = ' data-child="' + (trData[options.treeOptions.keys.childLen] || 0) + '" data-level="' + trData[options.treeOptions.keys.level] + '" class="datagrid-tree-tr datagrid-tree-level-' + trData[options.treeOptions.keys.level] + '"'
                            }
                        }

                        cls && (cls = ' class="' + cls + '"')

                        if (that.isTemplate) {
                            if (!n.delayRender) {
                                tempData[n.name] = (typeof render_label === 'undefined' ? label : render_label)
                            } else {
                                delete tempData[n.name]
                            }
                            if (!tds[0] && j == (modellength - 1)) {
                                if (tdTemplate && typeof tdTemplate === 'function')
                                    tdTemplate = tdTemplate.apply(that, [trData])

                                tdTemplate = that.tools.replacePlh4Template(tdTemplate, tempData, true)

                                if (that.options.hasChild && that.options.childOptions) {
                                    display = that.childColumn.hidden ? ' style="display:none;"' : ''
                                    tds[0] = ['<td data-title="..." align="center" class="datagrid-child-td"' + display + '><div>' + childFrag + '</div></td>']
                                }
                                if (that.options.showLinenumber) {
                                    display = that.linenumberColumn.hidden ? ' style="display:none;"' : ''

                                    var linenumbertd = ['<td data-title="No." align="center" class="datagrid-linenumber-td"' + display + '>' + linenumber + '</td>']

                                    tds[tds.length] = linenumbertd
                                }
                                if (that.options.showCheckboxcol) {
                                    display = that.checkboxColumn.hidden ? ' style="display:none;"' : ''

                                    var checkboxtd = ['<td data-title="Checkbox" align="center" class="datagrid-checkbox-td"' + display + '><div><input type="checkbox" data-toggle="icheck" name="datagrid.checkbox" value="true"></div></td>']

                                    tds[tds.length] = checkboxtd
                                }

                                var temptd = ['<td class="datagrid-template-td" colspan="' + tempcolspan + '">' + tdTemplate + '</td>']

                                tds[tds.length] = temptd

                                treeattr = ' class="datagrid-template-tr"'
                            }
                        } else {
                            tdHtml
                                .add('<td data-title="' + n.label + '"')
                                .add(align)
                                .add(cls)
                                .add(display)
                                .add('>')

                            if (tree) {
                                tdHtml.add(tree)
                            } else {
                                tdHtml
                                    .add('<div>')
                                    .add(typeof render_label === 'undefined' ? label : render_label)
                                    .add('</div>')
                            }

                            tdHtml.add('</td>')
                            tds.push(tdHtml.toString())
                        }
                    }

                    trs.push('<tr' + treeattr + '>' + tds.join('') + '</tr>')

                    if (isRenderOnly) {
                        trs = []
                        trs.push(tools.createNoDataTr(true) || '')
                    } else {
                        trs.push(tools.createChildTr(null, trData))
                    }
                }

                that.$tbody.html(trs.join(''))

                if (refreshFlag) {
                    that.initEvents()
                    if (options.editMode) that.edit()

                    if (!datas.length)
                        tools.createNoDataTr()

                    that.$boxP && that.$boxP.trigger('bjui.datagrid.paging.jump')

                    // locked
                    $.each(model, function (i, n) {
                        if (n.lock_refresh) {
                            that.colLock(n.th, true)
                            delete n.lock_refresh
                        }
                    })

                    setTimeout(function () {
                        that.$tableB.initui()
                        that.$lockTableB && that.$lockTableB.initui()

                        that.fixedHeight()

                        if (that.needfixedWidth) {
                            that.fixedWidth()
                            that.needfixedWidth = null
                        }

                        that.$boxM && that.$boxM.trigger('bjui.ajaxStop').hide()

                        // for initFilter
                        if (that.doInitFilter) {
                            that.tools.initFilter()
                            that.doInitFilter = undefined
                        }
                    }, datas.length + 1)
                }
            },
            coverTemplate: function () {
                var tools = this, options = that.options, datas = that.data, model = that.columnModel, trs

                if (that.isTemplate) {
                    trs = that.$grid.data('bjui.datagrid.trs.template')

                    if (!trs)
                        tools.createTrs(datas, true)
                    else {
                        that.$tbody.html(trs)

                        that.initEvents()
                        if (options.editMode) that.edit()

                        if (!datas.length)
                            tools.createNoDataTr()

                        that.$boxP && that.$boxP.trigger('bjui.datagrid.paging.jump')

                        // locked
                        $.each(model, function (i, n) {
                            if (n.lock_refresh) {
                                that.colLock(n.th, true)
                                delete n.lock_refresh
                            }
                        })

                        setTimeout(function () {
                            that.$tableB.initui()
                            that.$lockTableB && that.$lockTableB.initui()

                            that.fixedHeight()

                            that.$boxM && that.$boxM.trigger('bjui.ajaxStop').hide()

                            // for initFilter
                            if (that.doInitFilter) {
                                that.tools.initFilter()
                                that.doInitFilter = undefined
                            }
                        }, datas.length + 1)
                    }
                } else {
                    trs = that.$grid.data('bjui.datagrid.trs.normal')

                    if (!trs)
                        this.createTrs(datas, true)
                    else {
                        that.$tbody.html(trs)

                        that.initEvents()
                        if (options.editMode) that.edit()

                        if (!datas.length)
                            tools.createNoDataTr()

                        that.$boxP && that.$boxP.trigger('bjui.datagrid.paging.jump')

                        // locked
                        $.each(model, function (i, n) {
                            if (n.lock_refresh) {
                                that.colLock(n.th, true)
                                delete n.lock_refresh
                            }
                        })

                        setTimeout(function () {
                            that.$tableB.initui()
                            that.$lockTableB && that.$lockTableB.initui()

                            that.fixedHeight()

                            that.$boxM && that.$boxM.trigger('bjui.ajaxStop').hide()

                            // for initFilter
                            if (that.doInitFilter) {
                                that.tools.initFilter()
                                that.doInitFilter = undefined
                            }
                        }, datas.length + 1)
                    }
                }

                setTimeout(function () {
                    that.initFixedW = false
                    that.hasAutoCol = true
                    that.fixedWidth(true)
                }, 300)
            },
            replacePlh: function (url, data) {
                return url.replace(/{\/?[^}]*}/g, function ($1) {
                    var key = $1.replace(/[{}]+/g, ''), val = data[key]

                    if (typeof val === 'undefined' || val === 'null' || val === null)
                        val = ''

                    return val
                })
            },
            replacePlh4Template: function (html, data, replaceAll) {
                return html.replace(/{#\/?[^}]*}/g, function ($1) {
                    var key = $1.replace(/[{#}]+/g, ''), val = data[key]

                    if (replaceAll && typeof val === 'undefined')
                        return $1

                    if (typeof val === 'undefined' || val === 'null' || val === null)
                        val = ''

                    return val
                })
            },
            createNoDataTr: function (str) {
                //style="text-align:center;" 无数据  居中显示
                if (that.options.showNoDataTip) {
                    if (str) {
                        return '<tr class="datagrid-nodata"><td colspan="' + that.columnModel.length + '"  style="text-align:center;" >' + (typeof that.options.showNoDataTip === 'string' ? that.options.showNoDataTip : BJUI.getRegional('datagrid.noData')) + '</td></tr>'
                    } else if (!that.$tbody.find('> tr').length) {
                        $('<tr class="datagrid-nodata"></tr>').html('<td colspan="' + that.columnModel.length + '" style="text-align:center;">' + (typeof that.options.showNoDataTip === 'string' ? that.options.showNoDataTip : BJUI.getRegional('datagrid.noData')) + '</td>').appendTo(that.$tbody)
                    }
                }
            },
            createTreePlaceholder: function (data, label, isExpand) {
                var keys = that.options.treeOptions.keys, str = BJUI.StrBuilder()
                var addIndent = function (level) {
                    var indent = []

                    while (level--) {
                        indent.push('<span class="datagrid-tree-indent"></span>')
                    }

                    return indent.join('')
                }

                str.add('<div class="datagrid-tree-box">')

                if (data[keys.level])
                    str.add(addIndent(data[keys.level]))

                str.add('<span class="datagrid-tree-switch' + (typeof isExpand !== 'undefined' && !isExpand ? ' collapsed' : '') + '">')

                if (data[keys.isParent]) {
                    if (typeof isExpand === 'undefined') {
                        if (typeof data[keys.isExpand] === 'undefined')
                            data[keys.isExpand] = true

                        if (!data[keys.isExpand]) {
                            if (!that.collapseIndex) that.collapseIndex = []
                            that.collapseIndex.push(data.gridIndex)
                        }

                        str.add('<i class="fa fa-minus-square-o"></i>')
                    } else {
                        str.add('<i class="fa fa-' + (isExpand ? 'minus' : 'plus') + '-square-o"></i>')
                    }
                }

                str
                    .add('</span>')
                    .add('<span class="datagrid-tree-' + (data[keys.isParent] ? 'branch' : 'leaf') + '"><i class="fa fa-' + (data[keys.isParent] ? 'folder' : 'file') + '-o"></i></span>')
                    .add('<span class="datagrid-tree-title">')
                    .add(label)
                    .add('</span>')
                    .add('</div>')

                return str.toString()
            },
            createChildTr: function ($tr, trData) {
                if ($tr && $tr.next() && $tr.next().hasClass(that.classnames.tr_child))
                    return

                if (options.hasChild && options.childOptions && options.childOptions.dataUrl) {
                    that.childOptions = $.extend(true, {}, Datagrid.DEFAULTS, options.childOptions)

                    var child = '<tr class="' + that.classnames.tr_child + '"><td colspan="' + that.columnModel.length + '" style="width:100%; padding:10px;"><table class="table-child"></table></td></tr>'

                    if ($tr && $tr.length)
                        $tr.after(child)
                    else
                        return child
                }

                return ''
            },
            // Parameters can be (jQuery Object || number)
            getNoChildTrIndex: function (row) {
                var index

                if (isNaN(row)) {
                    if (!row || !row.length) return -1
                    index = row.index()
                } else {
                    index = parseInt(row, 10)
                }
                if (that.options.hasChild && that.options.childOptions)
                    index = index * 2

                return index
            },
            // Parameters can be (jQuery Object || number)
            getNoChildDataIndex: function (row) {
                var data_index

                if (isNaN(row))
                    data_index = this.getNoChildTrIndex(row)
                else
                    data_index = parseInt(row, 10)

                if (data_index === -1) return data_index
                if (that.options.hasChild && that.options.childOptions)
                    data_index = data_index / 2

                return data_index
            },
            // ajax load data by url
            loadData: function (data, refreshFlag) {
                var tools = this, url = options.dataUrl, dataType = options.dataType || 'json', model = that.columnModel

                that.$boxM && that.$boxM.show().trigger('bjui.ajaxStart')

                dataType = dataType.toLowerCase()
                if (dataType === 'array') dataType = 'text'

                if (options.postData) {
                    if (typeof options.postData === 'string') {
                        if (options.postData.trim().startsWith('{')) options.postData = options.postData.toObj()
                        else options.postData = options.postData.toFunc()
                    }
                    if (typeof options.postData === 'function') {
                        options.postData = options.postData.apply()
                    }
                    if (typeof options.postData === 'object') {
                        if (!data) data = options.postData
                        else data = $.extend({}, options.postData, data)
                    }
                }

                data = data || {}
                if (!data.pageSize && options.paging) {
                    data.pageSize = that.paging.pageSize
                    data.pageCurrent = that.paging.pageCurrent

                    //增加默认排序字段
                    if (!that.options.isTree) {
                        data.orders = that.paging.orders || "";
                    }
                }

                if (!options.paging) {
                    delete data.pageSize
                    delete data.pageCurrent
                }

                BJUI.ajax('doajax', {
                    url: url,
                    data: data,
                    type: options.loadType,
                    cache: options.cache || false,
                    dataType: dataType,
                    okCallback: function (response) {
                        if (dataType === 'json' || dataType === 'jsonp') {
                            tools.createTrsByData(response, refreshFlag)
                        } else if (dataType === 'text') {
                            if ($.type(response) !== 'array')
                                response = []

                            tools.createTrsByData(response, refreshFlag)
                        } else if (dataType === 'xml') {
                            var xmlData = [], obj

                            $(response).find('row').each(function () {
                                obj = {}

                                $(this).find('cell').each(function (i) {
                                    var $cell = $(this), label = $cell.text(), name = $cell.attr('name')

                                    obj[name] = label
                                })

                                xmlData.push(obj)
                            })

                            if (xmlData.length) tools.createTrsByData(xmlData, refreshFlag)
                        } else {
                            BJUI.debug('BJUI.Datagrid: The options \'dataType\' is incorrect!')
                        }
                    },
                    errCallback: function (json) {
                        if (json && json[BJUI.keys.statusCode]) {
                            BJUI.alertmsg('error', json[BJUI.keys.message] || BJUI.getRegional('datagrid.errorData'))
                        } else {
                            BJUI.alertmsg('warn', BJUI.getRegional('datagrid.errorData'))
                        }

                        tools.createTrsByData([], refreshFlag)
                    },
                    failCallback: function (msg) {
                        BJUI.alertmsg('warn', BJUI.getRegional('datagrid.failData'))

                        tools.createTrsByData([], refreshFlag)
                    }
                })
            },
            // append columns
            appendColumns: function () {
                that.childColumn = { name: that.options.keys.gridChild, gridChild: true, width: 30, minWidth: 30, label: '...', align: 'center', menu: false, edit: false, quicksort: false }
                that.linenumberColumn = { name: that.options.keys.gridNumber, gridNumber: true, width: 'auto', minWidth: 80, label: 'No.', align: 'center', menu: false, edit: false, quicksort: false }
                that.checkboxColumn = { name: that.options.keys.gridCheckbox, gridCheckbox: true, width: 30, minWidth: 30, label: 'Checkbox', align: 'center', menu: false, edit: false, quicksort: false }
                that.editBtnsColumn = { name: that.options.keys.gridEdit, gridEdit: true, width: that.options.customEditbtns.width, minWidth: 110, label: (typeof options.showEditbtnscol === 'string' ? options.showEditbtnscol : 'Edit'), align: 'center', menu: false, edit: false, hide: false, quicksort: false }
            },
            // setBoxb - height
            setBoxbH: function (height) {
                var boxH = height || that.boxH || options.height, topM = 0, h, $boxB, bH

                if (boxH < 100) return
                if (isNaN(boxH)) {
                    if (boxH === 'auto') {
                        $boxB = that.$boxB.clone().height('').prependTo('body').find('> colgroup').remove().end()
                        bH = $boxB.outerHeight()
                        $boxB.remove()

                        boxH = (that.$boxT ? that.$boxT.outerHeight() : 0)
                            + (that.$boxH ? that.$boxH.outerHeight() : 0)
                            + (that.$toolbar ? that.$toolbar.outerHeight() : 0)
                            + (that.$boxP ? that.$boxP.outerHeight() : 0)
                            + bH
                            + (that.$boxF ? that.$boxF.outerHeight() : 0)

                        if (options.maxHeight && Number(options.maxHeight) < boxH) {
                            boxH = Number(options.maxHeight)
                        }

                        that.$grid.height(boxH)
                        that.boxH = boxH
                    } else {
                        boxH = that.$grid.height()
                    }
                }

                if (that.$boxT) {
                    h = that.$boxT.outerHeight()
                    boxH -= h
                    topM += h
                }
                if (that.$toolbar) {
                    h = that.$toolbar.outerHeight()
                    boxH -= h
                    topM += h
                }
                if (that.$boxP)
                    boxH -= that.$boxP.outerHeight()
                if (that.$boxF)
                    boxH -= that.$boxF.outerHeight()

                topM += that.$tableH.outerHeight()
                boxH -= that.$boxH.outerHeight()

                if (boxH < 0) boxH = 0

                that.$boxB.height(boxH)
                that.$boxM.height(boxH).css({ top: topM })
                that.$lockB && that.$lockB.height(boxH)

                if (that.$element.data('bjui.datagrid.parent'))
                    that.$element.data('bjui.datagrid.parent').closest('table').datagrid('fixedHeight')
            },
            // column menu - toggle show submenu
            showSubMenu: function ($li, $menu, $submenu) {
                var left, width = $menu.outerWidth(), submenu_width = $submenu.data('width') || $submenu.outerWidth(), wh = that.$grid.height(), mt, submenu_height = $submenu.data('height'), animate_op, boxWidth = that.$boxH.width()
                var hidesubmenu = function ($li, $menu, $submenu) {
                    left = $menu.offset().left - that.$grid.offset().left - 1
                    animate_op = { left: '50%' }

                    $li.removeClass('active')

                    if ($menu.hasClass('position-right') || (boxWidth - left < width + submenu_width)) {
                        $submenu.css({ left: 'auto', right: '100%' })
                        animate_op = { right: '50%' }
                    } else {
                        $submenu.css({ left: '100%', right: 'auto' })
                    }
                    animate_op.opacity = 0.2

                    $submenu.stop().animate(animate_op, 'fast', function () {
                        $(this).hide()
                    })
                }

                $li.hover(function () {
                    $submenu.appendTo($li)
                    if ($li.hasClass(that.classnames.li_filter) && $submenu.is(':visible')) {
                        return false
                    } else {
                        var $filterli = $li.siblings('.' + that.classnames.li_filter)

                        if ($filterli.length && $filterli.hasClass('active')) {
                            hidesubmenu($filterli, $menu, $filterli.find('> .' + that.classnames.s_filter))
                        }
                    }

                    if ($li.hasClass(that.classnames.li_showhide)) {
                        mt = $li.position().top + $menu.position().top

                        if ((wh - mt) < submenu_height) {
                            if (submenu_height > wh) {
                                submenu_height = wh
                                $submenu.css('height', wh)
                            }
                            $submenu.css({ top: (- (submenu_height - (wh - mt))) })
                        }
                    }

                    left = $menu.offset().left - that.$grid.offset().left - 1
                    animate_op = { left: '100%' }

                    if ($menu.hasClass('position-right') || (boxWidth - left < width + submenu_width)) {
                        $submenu.css({ left: 'auto', right: '50%' })
                        animate_op = { right: '100%' }
                    } else {
                        $submenu.css({ left: '50%', right: 'auto' })
                    }
                    animate_op.opacity = 1

                    $li.addClass('active')
                    $submenu.show().stop().animate(animate_op, 'fast')
                }, function () {
                    if ($li.hasClass(that.classnames.li_filter)) {
                        return false
                    }
                    hidesubmenu($li, $menu, $submenu)
                })

                $li.on('hidesubmenu.bjui.datagrid.th', function (e, menu, submenu) {
                    hidesubmenu($(this), menu, submenu)
                })
            },
            // column menu - lock/unlock
            locking: function ($th) {
                var index = $th.data('index'), columnModel = that.columnModel[index], lockFlag = columnModel.lock, locked = columnModel.locked, $menu = that.$menu, $ul = $menu.find('> ul'), $lockli = $ul.find('> li.' + that.classnames.li_lock), $unlockli = $lockli.next()

                if (locked) {
                    $lockli.addClass('disable')
                    $unlockli.removeClass('disable')
                } else {
                    $unlockli.addClass('disable')
                    $lockli.removeClass('disable')
                }

                if (lockFlag) {
                    $lockli.show().off('click').on('click', function () {
                        if ($lockli.hasClass('disable')) return

                        $menu.hide().data('bjui.datagrid.menu.btn').removeClass('active')
                        that.colLock($th, true)
                    })

                    $unlockli.show().off('click').on('click', function () {
                        if ($unlockli.hasClass('disable')) return

                        $menu.hide().data('bjui.datagrid.menu.btn').removeClass('active')
                        that.colLock($th, false)
                    })
                } else {
                    $lockli.hide().off('click')
                    $unlockli.hide().off('click')
                }
            },
            // create show/hide column panel
            createShowhide: function () {
                var $showhide, keys = that.options.keys

                if (!that.$showhide) {
                    that.col_showhide_count = that.columnModel.length
                    $showhide = $('<ul class="' + that.classnames.s_showhide + '" role="menu"></ul>')

                    $.each(that.columnModel, function (i, n) {
                        if (that.tools.isGridModel(n))
                            that.col_showhide_count--

                        var $col = $(FRAG.gridShowhide.replaceAll('#index#', n.index).replaceAll('#label#', (n.label || ''))).attr('title', (n.label || '')).toggleClass('nodisable', !!(that.tools.isGridModel(n)))
                        var colClick = function (n) {
                            $col.click(function () {
                                if ($(this).hasClass('disable')) return false

                                var $this = $(this), check = !$this.find('i').hasClass('fa-check-square-o'), index = n.index

                                $this.toggleClass('datagrid-col-check')
                                    .find('i').attr('class', 'fa fa' + (check ? '-check' : '') + '-square-o')

                                that.showhideColumn(n.th, check)

                                if (!(that.tools.isGridModel(n))) {
                                    that.col_showhide_count = check ? that.col_showhide_count + 1 : that.col_showhide_count - 1
                                }

                                if (that.col_showhide_count == 1) $showhide.find('> li.datagrid-col-check').addClass('disable')
                                else $showhide.find('> li.disable').removeClass('disable')

                                $showhide.find('> li.nodisable').removeClass('disable')
                            })
                        }

                        colClick(n)
                        $col.appendTo($showhide)

                        if (n.hide) $col.trigger('click')
                    })

                    $showhide.appendTo(that.$grid)
                    $showhide.data('width', $showhide.outerWidth()).data('height', $showhide.outerHeight())
                    that.$showhide = $showhide
                }
            },
            // column - display/hide
            showhide: function (model, showFlag) {
                var keys = that.options.keys, index = model.index, $th = model.th, $trs = that.$tbody.find('> tr'), display = showFlag ? '' : 'none'
                var setColspan = function (column) {
                    var _colspan = column.colspan

                    if (showFlag) _colspan++
                    else _colspan--

                    if (!_colspan) column.th.css('display', 'none')
                    else column.th.css('display', '')

                    column.th.attr('colspan', _colspan)
                    column.colspan = _colspan

                    if (column.parent) setColspan(column.parent)
                }

                if (typeof model.hidden === 'undefined') model.hidden = false
                if (model.hidden === !showFlag) return

                model.hidden = !showFlag

                $th.css('display', display)

                if (that.options.tdTemplate && !this.isGridModel(model)) {
                    var colspan = $trs.eq(0).find('> td.datagrid-template-td').attr('colspan')

                    if (colspan) {
                        $trs.find('> td.datagrid-template-td').attr('colspan', parseInt(colspan, 10) + (model.hidden ? -1 : 1))
                    }
                } else {
                    $trs.find('> td:eq(' + index + ')').css('display', display)
                }
                that.$colgroupH.find('> col').eq(index).css('display', display)
                that.$colgroupB.find('> col').eq(index).css('display', display)
                that.$thead.find('> tr.datagrid-filter > th:eq(' + index + ')').css('display', display)
                if (that.$boxF) {
                    that.$tableF.find('> thead > tr > th:eq(' + index + ')').css('display', display)
                    that.$colgroupF.find('> col').eq(index).css('display', display)
                }

                if (model.calc) {
                    that.$tfoot && that.$tfoot.trigger('resizeH.bjui.datagrid.tfoot')
                }

                if (model.parent) setColspan(model.parent)

                // fixed width && height for child datagrid
                that.$tbody.find('> tr.' + that.classnames.tr_child + ':visible > td table').trigger('bjui.datagrid.child.resize')
            },
            isGridModel: function (model) {
                var keys = that.options.keys

                if (!model || typeof model !== 'object') return false
                return (model[keys.gridChild] || model[keys.gridNumber] || model[keys.gridCheckbox] || model[keys.gridEdit])
            },
            isGridData: function (name) {
                var keys = that.options.keys, b = false

                for (var key in keys) {
                    if (keys[key] === name) {
                        b = true

                        break
                    }
                }

                return b
            },
            // jump to page
            jumpPage: function (pageCurrent, pageSize) {
                var allData = that.allData, filterDatas

                if (pageCurrent) {
                    that.paging.oldPageCurrent = that.paging.pageCurrent
                    that.paging.pageCurrent = pageCurrent
                }
                if (pageSize) {
                    that.paging.oldPageSize = that.paging.pageSize

                    that.paging.pageSize = pageSize
                    that.paging.pageCount = this.getPageCount(pageSize, that.paging.total)

                    if (that.paging.pageCurrent > that.paging.pageCount)
                        that.paging.pageCurrent = that.paging.pageCount
                }

                if (options.local === 'remote') {
                    filterDatas = this.getRemoteFilterData(true)
                    this.loadData(filterDatas, true)
                } else {
                    this.initTbody(allData, true)
                }
            },
            // column - quicksort
            quickSort: function (model) {
                if (that.isDom) {
                    if (options.local === 'local') return
                    options.sortAll = true
                }
                if (!that.sortData) {
                    that.sortData = {}
                }
                if (that.$tbody.find('> tr.' + that.classnames.tr_edit).length) {
                    that.$tbody.alertmsg('info', BJUI.getRegional('datagrid.editMsg'))
                    return
                }

                var $th = model.th, data = that.data, allData = that.allData, postData, direction, name = model.name, type = model.type, $ths = that.$thead.find('> tr > th.datagrid-quicksort-th')

                if (!name) name = 'datagrid-noname'

                var getOrders = function () {
                    var orders = []

                    $.each(that.sortData, function (k, v) {
                        orders.push(k + ' ' + v.direction)
                    })

                    return orders
                }
                var sortData = function (data, orders) {
                    var localSort = function (a, b) {
                        var key_index = 0

                        function doCompare() {
                            var keys = orders[key_index].split(' '), name = keys[0], direction = keys[1], typeA = (typeof a[name]), typeB = (typeof b[name])

                            if (a[name] == b[name] && key_index < orders.length - 1) {
                                key_index++
                                return doCompare()
                            }

                            if ((typeA = typeB === 'number') || (typeA = typeB === 'boolean')) {
                                return direction === 'asc' ? (a[name] - b[name]) : (b[name] - a[name])
                            } else {
                                return direction === 'asc' ? String(a[name]).localeCompare(b[name]) : String(b[name]).localeCompare(a[name])
                            }
                        }

                        if (!orders || !orders.length) {
                            return (a['bjui_local_index'] - b['bjui_local_index'])
                        }
                        return doCompare()
                    }

                    data.sort(localSort)
                }

                if (options.fieldSortable)
                    $th.find('> div > .datagrid-label > i').remove()
                else
                    that.$thead.find('> tr:not(.datagrid-filter) > th > div > .datagrid-label > i').remove()

                if (model.sortDesc) {
                    model.sortDesc = false
                } else {
                    if (model.sortAsc) {
                        direction = 'desc'
                        model.sortAsc = false
                        model.sortDesc = true
                    } else {
                        direction = 'asc'
                        model.sortAsc = true
                    }
                    // for thead
                    $th.find('> div > .datagrid-label').prepend('<i class="datagrid-sort-i fa fa-long-arrow-' + (model.sortAsc ? 'up' : 'down') + '"></i>')
                }

                if (direction) {
                    if (that.sortData[name]) {
                        that.sortData[name]['direction'] = direction
                    } else {
                        that.sortData[name] = { index: (Object.keys(that.sortData)).length, direction: direction }
                    }
                } else {
                    delete that.sortData[name]
                }
                if (options.sortAll) {
                    if (options.local === 'remote') {
                        postData = that.$element.data('filterDatas') || {}
                        postData[BJUI.pageInfo.pageSize] = that.paging.pageSize
                        postData[BJUI.pageInfo.pageCurrent] = that.paging.pageCurrent

                        if (direction) {
                            postData[BJUI.pageInfo.orderField] = name
                            postData[BJUI.pageInfo.orderDirection] = direction
                        } else {
                            postData[BJUI.pageInfo.orderField] = ''
                            postData[BJUI.pageInfo.orderDirection] = ''
                        }

                        postData['orders'] = getOrders().join(',')
                        that.orders = { orders: postData['orders'] }

                        this.loadData(that.tools.getRemoteFilterData(true), true)
                    } else {
                        sortData(allData, getOrders())
                        this.initTbody(allData, true)
                    }
                } else {
                    sortData(data, getOrders())
                    this.createTrs(data, true)
                }
            },
            showFilterMsg: function (msgs) {
                var $msg = that.$boxH.find('.datagrid-thead-dialog-filter-msg'), $filter = $msg.find('> .msg-filter'), html = BJUI.StrBuilder()

                $filter.html('')

                if (!msgs.length) return

                $.each(msgs, function (i, n) {
                    if (!n) return true

                    var label = n.label, val = n.data.valA

                    if (n.model.type === 'select' || n.model.type === 'boolean') {
                        val = n.model.render.call(that, val, {}, n.model.items, n.model.itemattr)
                    }

                    html.add('；')
                        .add(n.model.label + ':' + val)
                })

                html = html.toString()
                html.length > 1 && (html = html.substr(1))

                $filter.html(html)
            },
            quickFilter: function (model, filterDatas) {
                if (that.isDom) {
                    if (options.local != 'remote') {
                        BJUI.debug('BJUI.Datagrid: Please change the local option is remote!')
                        return
                    }
                    if (!options.dataUrl) {
                        BJUI.debug('BJUI.Datagrid: Not Set the dataUrl option!')
                        return
                    }
                }

                var tools = this, $th = model.th, data = that.data, allData = that.allData, name = model.name, postData, fDatas, msgs = []
                var switchOperator = function (operator, val1, val2, model) {
                    var compare = false

                    switch (operator) {
                        case '=':
                            compare = String(val1) === String(val2)
                            break
                        case '!=':
                            compare = String(val1) !== String(val2)
                            break
                        case '>':
                            compare = parseFloat(val2) > parseFloat(val1)
                            break
                        case '<':
                            compare = parseFloat(val2) < parseFloat(val1)
                            break
                        case 'like':
                            if (model && model.type === 'select') {
                                compare = String(val1) === String(val2)
                                if (model.attrs && model.attrs.multiple) {
                                    if ($.isArray(val1) && String(val2)) {
                                        $.each(String(val2).split(','), function (i, n) {
                                            compare = $.inArray(n, val1) != -1
                                            if (compare) return false
                                        })
                                    }
                                }
                            } else {
                                compare = String(val2).indexOf(String(val1)) >= 0
                            }
                            break
                        default:
                            break
                    }

                    return compare
                }
                var filterData = function (data, filterDatas) {
                    var grepFun = function (n) {
                        var count = 0

                        $.each(filterDatas, function (name, v) {
                            var op = v.datas

                            count++
                            if (!op) {
                                count--
                                v.model.isFiltered = false
                                v.model.th.trigger('filter.bjui.datagrid.th', [false])
                                if (v.model.$quickfilter) v.model.$quickfilter.trigger('clearfilter.bjui.datagrid.thead')

                                return true
                            }

                            v.model.isFiltered = true
                            v.model.th.trigger('filter.bjui.datagrid.th', [true])

                            if (!msgs[v.model.index])
                                msgs[v.model.index] = { model: v.model, data: v.datas }

                            if (op.andor) {
                                if (op.andor === 'and') {
                                    if (switchOperator(op.operatorA, op.valA, n[name], v.model) && switchOperator(op.operatorB, op.valB, n[name], v.model)) {
                                        count--
                                    }
                                } else if (op.andor === 'or') {
                                    if (switchOperator(op.operatorA, op.valA, n[name], v.model) || switchOperator(op.operatorB, op.valB, n[name], v.model)) {
                                        count--
                                    }
                                }
                            } else {
                                if (op.operatorB) {
                                    if (switchOperator(op.operatorB, op.valB, n[name], v.model)) {
                                        count--
                                    }
                                } else {
                                    if (switchOperator(op.operatorA, op.valA, n[name], v.model)) {
                                        count--
                                    }
                                }
                            }
                        })

                        return !count ? true : false
                    }

                    return $.grep(data, function (n, i) {
                        return grepFun(n)
                    })
                }

                if (!that.filterDatas) that.filterDatas = {}
                if (options.filterMult) {
                    that.filterDatas[name] = { datas: filterDatas, model: model }
                } else {
                    that.filterDatas = {}
                    that.filterDatas[name] = { datas: filterDatas, model: model }
                }

                if (options.local !== 'remote' && allData) {
                    if (!that.oldAllData) that.oldAllData = allData.concat()
                    else allData = that.oldAllData.concat()
                }

                if (options.filterAll) {
                    if (options.local === 'remote') {
                        tools.loadData(tools.getRemoteFilterData(false, msgs), true)
                    } else {
                        fDatas = filterData(allData, that.filterDatas)

                        that.tools.showFilterMsg(msgs)

                        that.paging.pageCurrent = 1
                        that.paging.pageCount = this.getPageCount(that.paging.pageSize, fDatas.length)

                        this.initTbody(fDatas, true)
                    }
                } else {
                    if (that.isDom) {
                        tools.loadData(tools.getRemoteFilterData(false, msgs), true)
                    } else {
                        if (!that.oldData) that.oldData = data.concat()
                        else data = that.oldData.concat()

                        fDatas = filterData(data, that.filterDatas)

                        that.tools.showFilterMsg(msgs)

                        this.createTrs(fDatas, true)
                    }
                }
            },
            getRemoteFilterData: function (isPaging, msgs) {
                var filterDatas = {}

                if (that.filterDatas && !$.isEmptyObject(that.filterDatas)) {
                    $.each(that.filterDatas, function (name, v) {
                        if (!v.datas) {
                            v.model.isFiltered = false
                            v.model.th.trigger('filter.bjui.datagrid.th', [false])
                            if (v.model.$quickfilter) v.model.$quickfilter.trigger('clearfilter.bjui.datagrid.thead')
                            msgs && msgs[v.model.index] && (msgs[v.model.index] = false)
                            return true
                        }

                        v.model.isFiltered = true
                        v.model.th.trigger('filter.bjui.datagrid.th', [true])

                        if (options.jsonPrefix)
                            name = options.jsonPrefix + '.' + name

                        if (v.datas.andor)
                            filterDatas[name + '.andor'] = v.datas.andor
                        if (v.datas.operatorA) {
                            filterDatas[name] = v.datas.valA
                            filterDatas[name + '.operator'] = v.datas.operatorA
                        }
                        if (v.datas.operatorB) {
                            if (filterDatas[name]) {
                                filterDatas[name] = [filterDatas[name], v.datas.valB]
                                filterDatas[name + '.operator'] = [filterDatas[name + '.operator'], v.datas.operatorB]
                            } else {
                                filterDatas[name] = v.datas.valB
                                filterDatas[name + '.operator'] = v.datas.operatorB
                            }
                        }
                    })

                    if (!isPaging) that.paging.pageCurrent = 1
                }

                if (that.initFilter) {
                    var name = options.jsonPrefix ? options.jsonPrefix + '.' : ''

                    $.each(that.initFilter, function (k, v) {
                        if (!that.filterDatas || typeof that.filterDatas[k] === 'undefined')
                            filterDatas[name + k] = v.value
                    })
                }

                // paging
                filterDatas[BJUI.pageInfo.pageSize] = that.paging.pageSize
                filterDatas[BJUI.pageInfo.pageCurrent] = that.paging.pageCurrent || 1
                if (!that.options.isTree) {
                    filterDatas["orders"] = that.paging.orders || "";
                }

                $.extend(filterDatas, that.orders || {})

                that.$element.data('filterDatas', filterDatas)

                return filterDatas
            },
            // set data for Dom
            setDomData: function (tr) {
                var columnModel = that.columnModel, data = {}, hideDatas = tr.attr('data-hidden-datas'), attach = that.attach

                tr.find('> td').each(function (i) {
                    var $td = $(this), model = columnModel[i], val = $td.attr('data-val') || $td.text()

                    if (!model.name) data['datagrid-noname' + i] = val
                    else data[model.name] = val
                })

                if (hideDatas) hideDatas = hideDatas.toObj()

                attach[that.options.keys.gridNumber] = (tr.index() + 1)
                $.extend(data, attach, hideDatas)

                tr.data('initData', data)

                return data
            },
            // update linenumber
            updateLinenumber: function () {
                if ($.inArray(that.linenumberColumn, that.columnModel) == -1)
                    return

                var lock = false

                if (that.linenumberColumn.locked) {
                    that.colLock(that.linenumberColumn.th, false)
                    lock = true
                }

                that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')').each(function (i) {
                    var $tr = $(this)

                    $tr.find('> td.' + that.classnames.td_linenumber).text(i + 1)
                })

                if (lock)
                    that.colLock(that.linenumberColumn.th, true)
            },
            // update that.data gridIndex && gridNumber
            updateGridIndex: function () {
                var paging = that.paging, options = that.options, startNumber = (paging.pageCurrent - 1) * paging.pageSize

                $.each(that.data, function (i, data) {
                    var linenumber = options.linenumberAll ? (startNumber + (i + 1)) : (i + 1)

                    data[options.keys.gridNumber] = linenumber
                    data[options.keys.gridIndex] = i
                })
            },
            // init filter
            initFilter: function ($input, model) {
                var initFilter = that.initFilter
                var doFilter = function ($input, model) {
                    $input.val(String(that.options.initFilter[model.name]))

                    if (that.$headFilterUl) {
                        var $headinput = that.$headFilterUl.find('> li.li-' + model.index + '> ' + $input[0].tagName)

                        if ($headinput.length) {
                            $headinput.val($input.val())
                            if ($headinput.isTag('select'))
                                $headinput.selectpicker('refresh')
                        }
                    }

                    if (that.options.local === 'remote') {
                        model.isFiltered = true
                        model.th.trigger('filter.bjui.datagrid.th', [true])
                    } else {
                        that.tools.quickFilter(model, { operatorA: 'like', valA: that.options.initFilter[model.name] })
                    }
                }

                if (!$input && !model) {
                    if (initFilter) {
                        $.each(initFilter, function (i, n) {
                            doFilter(n.input, n.model)
                        })
                    }
                    return
                }

                if (!initFilter) initFilter = {}

                if (!model) return

                doFilter($input, model)

                if (!initFilter[model.name]) {
                    initFilter[model.name] = {}
                    initFilter[model.name].input = $input
                    initFilter[model.name].model = model
                    initFilter[model.name].value = that.options.initFilter[model.name]
                }

                that.initFilter = initFilter
            },
            // init inputs array for edit
            initEditInputs: function () {
                var columnModel = that.columnModel

                that.inputs = []

                $.each(columnModel, function (i, op) {
                    var name = op.name, rule = '', pattern = '', selectoptions = [], attrs = ''

                    if (!op) return
                    if (op.attrs && typeof op.attrs === 'object') {
                        $.each(op.attrs, function (i, n) {
                            if (typeof n === 'object') n = JSON.stringify(n).replaceAll('\"', '\'')
                            attrs += ' ' + i + '=' + n
                        })
                    }

                    if (op === that.childColumn || op === that.linenumberColumn || op === that.checkboxColumn || op === that.editBtnsColumn) {
                        that.inputs.push('')
                    } else if (name) {
                        if (op.rule) rule = ' data-rule="' + op.label + '：' + op.rule + '"'
                        else if (op.type === 'date') rule = ' data-rule="pattern(' + (op.pattern || 'yyyy-MM-dd') + ')"';
                        if (op.type) {
                            switch (op.type) {
                                case 'date':
                                    if (!op.pattern) op.pattern = 'yyyy-MM-dd'
                                    pattern = ' data-pattern="' + op.pattern + '"'
                                    that.inputs.push('<input type="text" name="' + name + '" data-toggle="datepicker"' + pattern + rule + attrs + '>')

                                    break
                                case 'select':
                                    if (!op.items) return

                                    $.each(op.items, function (i, n) {
                                        if (op.itemattr) {
                                            selectoptions.push('<option value="' + n[op.itemattr.value] + '">' + n[op.itemattr.label] + '</option>')
                                        } else {
                                            $.each(n, function (key, value) {
                                                selectoptions.push('<option value="' + key + '">' + value + '</option>')
                                            })
                                        }
                                    })

                                    that.inputs.push('<select name="' + name + '" data-toggle="selectpicker"' + rule + attrs + ' data-width="100%">' + selectoptions.join('') + '</select>')

                                    break
                                case 'boolean':
                                    that.inputs.push('<input type="checkbox" name="' + name + '" data-toggle="icheck"' + rule + attrs + ' value="true">')

                                    break
                                case 'findgrid':
                                    that.inputs.push('<input type="text" name="' + name + '" data-toggle="findgrid" data-custom-event="true"' + rule + attrs + '>')

                                    break
                                case 'tags':
                                    that.inputs.push('<input type="text" name="' + name + '" data-toggle="tags"' + attrs + '>')

                                    break
                                case 'spinner':
                                    that.inputs.push('<input type="text" name="' + name + '" data-toggle="spinner"' + rule + attrs + '>')

                                    break
                                case 'textarea':
                                    that.inputs.push('<textarea data-toggle="autoheight" rows="1"' + rule + attrs + '></textarea>')

                                    break
                                default:
                                    that.inputs.push('<input type="text" name="' + name + '"' + rule + attrs + '>')
                                    break
                            }
                        } else {
                            that.inputs.push('<input type="text" name="' + name + '"' + rule + attrs + '>')
                        }
                    } else {
                        that.inputs.push('')
                    }
                })

                return that.inputs
            },
            contextmenuH: function () {
                var tools = this

                that.$tableH.on('contextmenu', 'tr:not(.datagrid-filter)', function (e) {
                    if (!that.$showhide) tools.createShowhide()

                    var posX = e.pageX, posY = e.pageY, wh = $(window).height(), mh = that.$showhide.data('height')

                    if ($(window).width() < posX + that.$showhide.width()) posX -= that.$showhide.width()
                    if (posY < 0 || (posY + mh) > wh)
                        posY = 0
                    if (mh > wh)
                        that.$showhide.css({ height: wh })
                    else
                        that.$showhide.css({ height: '' })
                    if (that.$menu) {
                        that.$grid.trigger('click.bjui.datagrid.filter')
                    }

                    that.$showhide
                        .appendTo('body')
                        .css({ left: posX, top: posY, opacity: 1, 'z-index': 9999 }).show()

                    $(document).on('click', function (e) {
                        var $showhide = $(e.target).closest('.' + that.classnames.s_showhide)

                        if (!$showhide.length)
                            that.$showhide.css({ left: '50%', top: 0, opacity: 0.2, 'z-index': '' }).hide().appendTo(that.$grid)
                    })

                    e.preventDefault()
                    e.stopPropagation()
                })
            },
            contextmenuB: function ($tr, isLock) {
                $tr.contextmenu('show',
                    {
                        exclude: 'input, .bootstrap-select',
                        items: [
                            {
                                icon: 'refresh',
                                title: BJUI.getRegional('datagrid.refresh'),
                                func: function (parent, menu) {
                                    that.refresh()
                                }
                            },
                            {
                                title: 'diver'
                            },
                            {
                                icon: 'plus',
                                title: BJUI.getRegional('datagrid.add'),
                                func: function (parent, menu) {
                                    that.add()
                                }
                            },
                            {
                                icon: 'edit',
                                title: BJUI.getRegional('datagrid.edit'),
                                func: function (parent, menu) {
                                    var $tr = parent

                                    if (isLock) $tr = that.$tbody.find('> tr:eq(' + $tr.index() + ')')
                                    that.doEditRow($tr)
                                }
                            },
                            {
                                icon: 'undo',
                                title: BJUI.getRegional('datagrid.cancel'),
                                func: function (parent, menu) {
                                    var $tr = parent

                                    if (isLock) $tr = that.$tbody.find('> tr:eq(' + $tr.index() + ')')

                                    if (!$tr.hasClass(that.classnames.tr_edit)) {
                                        $tr = that.$tbody.find('> tr.' + that.classnames.tr_edit)
                                    }
                                    that.doCancelEditRow($tr)
                                }
                            },
                            {
                                icon: 'remove',
                                title: BJUI.getRegional('datagrid.del'),
                                func: function (parent, menu) {
                                    var $tr = parent

                                    if (isLock) $tr = that.$tbody.find('> tr:eq(' + $tr.index() + ')')
                                    that.delRows($tr)
                                }
                            }
                        ]
                    }
                )
            }
        }

        return tools
    }

    Datagrid.prototype.init = function () {
        if (!this.$element.isTag('table')) return
        if (this.$element.data('bjui.datagrid.init')) return

        this.$element.data('bjui.datagrid.init', true)

        var that = this, options = that.options, keys = options.keys, tools = that.tools, $parent = that.$element.parent(), gridHtml = BJUI.StrBuilder()

        options.tableWidth = options.tableWidth || ''
        options.width = options.width || '100%'
        options.height = options.height || 'auto'
        that.isDom = false
        that.columnModel = []
        that.inputs = []
        that.regional = BJUI.regional.datagrid

        gridHtml
            .add('<div class="bjui-datagrid">')
            .add(options.gridTitle ? '<div class="datagrid-title">' + options.gridTitle + '</div>' : '')
            .add(options.showToolbar ? '<div class="datagrid-toolbar"></div>' : '')
            .add('<div class="datagrid-box-h"><div class="datagrid-wrap-h"><table class="table table-bordered"><colgroup></colgroup></table></div></div>')
            .add('<div class="datagrid-box-b"><div class="datagrid-wrap-b"></div></div>')
            .add('<div class="datagrid-box-m"></div>')
            .add(options.paging ? '<div class="datagrid-paging-box bjui-paging-box"></div>' : '')
            .add('</div>')

        that.$grid = $(gridHtml.toString()).insertAfter(that.$element).css('height', options.height)
        that.$boxH = that.$grid.find('> div.datagrid-box-h')
        that.$boxB = that.$boxH.next()
        that.$boxM = that.$boxB.next().css('height', options.height)
        that.$boxP = options.paging ? that.$boxM.next() : null
        that.$boxT = options.gridTitle ? that.$grid.find('> div.datagrid-title') : null
        that.$toolbar = options.showToolbar ? that.$boxH.prev() : null
        that.$tableH = that.$boxH.find('> div > table')
        that.$tableB = that.$element
        // tdtemplate
        that.isTemplate = (options.tdTemplate && options.templateWidth) && options.templateWidth > that.$grid.width() || (options.tdTemplate && !options.templateWidth)

        that.$grid.data('bjui.datagrid.table', that.$element.clone())
        that.$boxB.find('> div').append(that.$element)

        that.initTop()

        if (typeof options.paging === 'string') options.paging = options.paging.toObj()
        that.paging = $.extend({}, { pageSize: 30, selectPageSize: '30,60,90', pageCurrent: 1, total: 0, showPagenum: 5 }, (typeof options.paging === 'object') && options.paging)
        that.$thead = that.$element.find('> thead')
        that.$tbody = that.$element.find('> tbody')
        that.attach = {}

        that.attach[keys.gridNumber] = 0
        that.attach[keys.gridCheckbox] = '#checkbox#'
        that.attach[keys.gridEdit] = '#edit#'

        if (that.$tbody && that.$tbody.find('> tr').length) {
            that.isDom = true

            that.setColumnModel()

            that.$tbody.find('> tr > td').each(function () {
                var $td = $(this), html = $td.html()

                $td.html('<div>' + html + '</div>')
            })

            if (!that.paging.total) {
                that.paging.total = that.$tbody.find('> tr').length
                that.paging.pageCount = 1
            } else {
                that.paging.pageCount = tools.getPageCount(that.paging.pageSize, that.paging.total)
            }

            that.paging.pageCurrent = 1
            that.initThead()
        } else {
            that.$tbody = null
            that.$element.find('> tbody').remove()

            if (options.columns) {
                if (typeof options.columns === 'string') {
                    if (options.columns.trim().startsWith('[')) {
                        options.columns = options.columns.toObj()
                    } else {
                        options.columns = options.columns.toFunc()
                    }
                }
                if (typeof options.columns === 'function') {
                    options.columns = options.columns.call()
                }

                that.$thead = null
                that.$element.find('> thead').remove()
                that.createThead()
            } else {
                if (that.$thead && that.$thead.length && that.$thead.find('> tr').length) {
                    that.setColumnModel()
                } else {
                    BJUI.debug('BJUI.Datagrid: No set options \'columns\' !')
                    that.destroy()
                    return
                }
            }
            if (options.data || options.dataUrl) {
                that.createTbody()
            } else {
                BJUI.debug('BJUI.Datagrid: No options \'data\' or \'dataUrl\'!')
                that.destroy()
            }
        }
    }

    // DOM to datagrid - setColumnModel
    Datagrid.prototype.setColumnModel = function () {
        var that = this, options = that.options, $trs = that.$thead.find('> tr'), rows = [], ths = [], trLen = $trs.length

        if (!that.isDom) {
            that.tools.appendColumns()

            var $th, _rowspan = trLen > 1 ? ' rowspan="' + trLen + '"' : ''

            if (options.showCheckboxcol) {
                that.columnModel.push(that.checkboxColumn)
                if (options.showCheckboxcol === 'lock') that.checkboxColumn.initLock = true

                $th = $('<th class="' + that.classnames.td_checkbox + '"' + _rowspan + '><input type="checkbox" data-toggle="icheck"></th>')
                $th.prependTo($trs.first())
                if (_rowspan) $th.data('datagrid.column', that.checkboxColumn)
            }
            if (options.showLinenumber) {
                that.columnModel.unshift(that.linenumberColumn)
                if (options.showLinenumber === 'lock') that.linenumberColumn.initLock = true

                $th = $('<th class="' + that.classnames.td_linenumber + '"' + _rowspan + '>No.</th>')
                $th.prependTo($trs.first())
                if (_rowspan) $th.data('datagrid.column', that.linenumberColumn)
            }
            if (options.showChildcol || (options.showChildcol === undefined && options.hasChild && options.childOptions)) {
                that.columnModel.unshift(that.childColumn)

                $th = $('<th class="' + that.classnames.td_child + '"' + _rowspan + '>...</th>')
                $th.prependTo($trs.first())
                if (_rowspan) $th.data('datagrid.column', that.childColumn)
            }
            if (options.showEditbtnscol) {
                $th = $('<th' + _rowspan + '>' + that.editBtnsColumn.label + '</th>')
                $th.appendTo($trs.first())
                if (_rowspan) $th.data('datagrid.column', that.editBtnsColumn)
                that.columnModel[$th.index()] = that.editBtnsColumn
            }
        }

        if ($trs.length && trLen == 1) {
            $trs.find('> th').each(function (i) {
                var $th = $(this).addClass('single-row').data('index', i), op = $th.data('options'), oW = $th.attr('width') || 'auto', label = $th.html()

                if (that.columnModel.length && that.columnModel[i]) {
                    op = that.columnModel[i]
                    op.index = i
                } else {
                    if (op && typeof op === 'string') op = op.toObj()
                    if (typeof op !== 'object') op = {}

                    op.index = i
                    op.label = label
                    op.width = (typeof op.width === 'undefined') ? oW : op.width

                    op = that.tools.setOp(op)

                    that.columnModel[i] = op
                }

                that.columnModel[i].th = $th

                $th.html('<div><div class="datagrid-space"></div><div class="datagrid-label">' + label + '</div><div class="' + that.classnames.th_cell + '"><div class="' + that.classnames.th_resizemark + '"></div></div></div>')
                if (op.menu && options.columnMenu) $th.addClass(that.classnames.th_menu)
                if (options.fieldSortable && op.quicksort) $th.addClass('datagrid-quicksort-th')
                if (op.align) $th.attr('align', op.align)
            })
        } else { // multi headers
            $trs.each(function (len) {
                var next_rows = [], next_ths = [], index = -1, next_index = 0

                if (rows.length) {
                    next_rows = rows.concat()
                    next_ths = ths.concat()
                }
                rows = []
                ths = []

                $(this).find('> th').each(function (i) {
                    var $th = $(this), op = $th.data('options') || $th.data('datagrid.column') || {}, colspan = parseInt(($th.attr('colspan') || 0), 10), rowspan = parseInt(($th.attr('rowspan') || 0), 10), oW = $th.attr('width') || 'auto', label = $th.html()

                    if (op && typeof op === 'string') op = op.toObj()
                    if (typeof op !== 'object') op = {}
                    if (BJUI.isIE(8) && colspan === 1) colspan = 0

                    op.label = label
                    op.th = $th
                    if (op[options.keys.gridCheckbox]) op.label = 'Checkbox'

                    index++
                    if (colspan) {
                        op.colspan = colspan
                        for (var start_index = (next_rows.length ? next_rows[index] : index), k = start_index; k < (start_index + colspan); k++) {
                            rows[next_index++] = k
                            ths[next_index - 1] = op
                        }
                        index += (colspan - 1)

                        $th.data('index', index)

                        if (next_rows.length) {
                            op.parent = next_ths[index]
                        }
                    }
                    if (!rowspan || rowspan == 1) $th.addClass('single-row')
                    if (!colspan) {
                        op.width = (typeof op.width === 'undefined') ? oW : op.width

                        op = that.tools.setOp(op)
                        $th.html('<div><div class="datagrid-space"></div><div class="datagrid-label">' + label + '</div><div class="' + that.classnames.th_cell + '"><div class="' + that.classnames.th_resizemark + '"></div></div></div>')

                        if (op.menu && options.columnMenu) $th.addClass(that.classnames.th_menu)
                        if (options.fieldSortable && op.quicksort) $th.addClass('datagrid-quicksort-th')
                        if (op.align) $th.attr('align', op.align)
                        if (!next_rows.length) {
                            op.index = index
                            that.columnModel[index] = op
                        } else {
                            op.index = next_rows[index]
                            op.parent = next_ths[index]
                            that.columnModel[next_rows[index]] = op
                        }

                        $th.data('index', op.index)
                    } else {
                        $th.html('<div><div class="datagrid-space"></div><div class="datagrid-label">' + label + '</div><div class="' + that.classnames.th_cell + '"><div class="' + that.classnames.th_resizemark + '"></div></div></div>')
                    }
                })
            })
        }
    }

    // create thead by columns
    Datagrid.prototype.createThead = function () {
        var that = this, options = that.options, keys = options.keys, columns = options.columns.concat(), rowArr = [], rows = [], label, align, width, colspan, rowspan, resize, menu,
            columns2Arr = function (columns, rowArr, index, parent) {
                if (!rowArr) rowArr = []
                if (!index) index = 0
                if (!rowArr[index]) rowArr[index] = []

                $.each(columns, function (i, n) {
                    var len = rowArr[index].length, colspan

                    if (parent) n.parent = parent
                    if (n.columns) {
                        colspan = n.columns.length
                        if (index && n.parent) {
                            that.tools.setColspan(n.parent, colspan)
                        }

                        n.index = that.columnModel.length + colspan - 1
                        n.colspan = colspan
                        n.quicksort = false
                        rowArr[index][len++] = n

                        return columns2Arr(n.columns, rowArr, index + 1, n)
                    } else {
                        n.rowspan = index
                        n.index = that.columnModel.length

                        n = that.tools.setOp(n)

                        rowArr[index][len++] = n
                        that.columnModel.push(n)
                    }
                })

                return rowArr
            }

        that.tools.appendColumns()

        if (options.showCheckboxcol) {
            columns.unshift(that.checkboxColumn)
            if (options.showCheckboxcol === 'lock') that.checkboxColumn.initLock = true
        }
        if (options.showLinenumber) {
            columns.unshift(that.linenumberColumn)
            if (options.showLinenumber === 'lock') that.linenumberColumn.initLock = true
        }
        if (options.showChildcol || (options.showChildcol === undefined && options.hasChild && options.childOptions)) {
            columns.unshift(that.childColumn)
        }
        if (options.showEditbtnscol) columns.push(that.editBtnsColumn)

        rowArr = columns2Arr(columns, rowArr)
        // the last model can't lock
        that.columnModel[that.columnModel.length - (options.showEditbtnscol ? 2 : 1)].lock = false
        // hidden fields
        if (options.hiddenFields) that.hiddenFields = options.hiddenFields
        // create thead
        that.$thead = $('<thead></thead>')
        $.each(rowArr, function (i, arr) {
            var $tr = $('<tr style="height:25px;"></tr>'), $num = '<th class="datagrid-number"></th>', $th

            $.each(arr, function (k, n) {
                label = n.label || n.name
                align = n.align ? (' align="' + n.align + '"') : ''
                width = n.width ? (' width="' + n.width + '"') : ''
                colspan = n.colspan ? ' colspan="' + n.colspan + '"' : ''
                rowspan = (rowArr.length - n.rowspan > 1) ? ' rowspan="' + (rowArr.length - n.rowspan) + '"' : ''
                resize = '<div class="' + that.classnames.th_resizemark + '"></div>'
                menu = ''

                if (n[keys.gridCheckbox]) label = '<input type="checkbox" data-toggle="icheck">'
                if (n.colspan) align = ' align="center"'
                if (n.thalign) align = ' align="' + n.thalign + '"'
                if (n.menu && options.columnMenu) menu = ' class="' + that.classnames.th_menu + '"'

                $th = $('<th' + menu + width + align + colspan + rowspan + '><div><div class="datagrid-space"></div><div class="datagrid-label">' + label + '</div><div class="' + that.classnames.th_cell + '">' + resize + '</div></div></th>')
                $th.data('index', n.index).appendTo($tr)

                if (!rowspan) $th.addClass('single-row')
                if (n[keys.gridChild]) $th.addClass(that.classnames.td_child)
                if (n[keys.gridNumber]) $th.addClass(that.classnames.td_linenumber)
                if (n[keys.gridCheckbox]) $th.addClass(that.classnames.td_checkbox)
                if (options.fieldSortable && n.quicksort) $th.addClass('datagrid-quicksort-th')

                n.th = $th
            })

            $tr.appendTo(that.$thead)
        })

        that.$thead.appendTo(that.$element).initui()
    }

    Datagrid.prototype.createTbody = function () {
        var that = this, options = that.options, data = options.data, model = that.columnModel, cols = []

        if (data) {
            if (typeof data === 'string') {
                if (data.trim().startsWith('[') || data.trim().startsWith('{')) {
                    data = data.toObj()
                } else {
                    data = data.toFunc()
                }
            }
            if (typeof data === 'function') {
                data = data.call()
            }

            options.data = data
            that.tools.createTrsByData(data)
        } else if (options.dataUrl) {
            var data = {}

            if (typeof options.initFilter === 'object' && options.initFilter && options.jsonPrefix) {
                $.each(options.initFilter, function (k, v) {
                    data[options.jsonPrefix + '.' + k] = v
                })
            }

            that.$element.data('filterDatas', data)

            that.tools.loadData(data)
        }
    }

    Datagrid.prototype.refresh = function (filterFlag) {
        var that = this, options = that.options, tools = that.tools, isDom = that.isDom, pageInfo = BJUI.pageInfo, paging = that.paging, postData = {}

        if (!options.dataUrl) {
            if (options.data && options.data.length) {
                tools.initTbody(that.allData, true)
                return
            }

            BJUI.debug('BJUI.Datagrid: Not Set the dataUrl option!')
            return
        }

        if (!options.postData || !options.postData[pageInfo.pageSize]) {
            postData[pageInfo.pageSize] = paging.pageSize
            postData[pageInfo.pageCurrent] = paging.pageCurrent

            //增加默认排序字段
            postData["orders"] = paging.orders || "";
        }
        if (options.initFilter && typeof options.initFilter === 'object') {
            if (typeof options.initFilter === 'object' && options.initFilter && options.jsonPrefix) {
                $.each(options.initFilter, function (k, v) {
                    postData[options.jsonPrefix + '.' + k] = v
                })
            }
            that.doInitFilter = true
        }
        if (filterFlag)
            $.extend(postData, that.$element.data('filterDatas') || {})

        tools.loadData(postData, true)

        // clear fiter
        if (!filterFlag) {
            that.filterDatas = null
            $.each(that.columnModel, function (i, n) {
                n.th.trigger('filter.bjui.datagrid.th', [false])
                n.isFiltered = false
                if (n.$quickfilter) n.$quickfilter.trigger('clearfilter.bjui.datagrid.thead')
            })
            // clear sort
            that.orders = {}
            that.$thead.find('> tr > th.datagrid-quicksort-th').find('> div > .datagrid-label > i').remove()
        }
    }

    Datagrid.prototype.refreshParent = function (filterFlag) {
        var that = this, $parent = that.$element.data('bjui.datagrid.parent')

        if ($parent && $parent.length)
            $parent.closest('table').datagrid('refresh', filterFlag)
    }

    Datagrid.prototype.refreshChild = function (row, filterFlag) {
        var that = this, $trs = that.$tbody.find('> tr'), $table,
            refresh = function (obj) {
                $table = obj.data('bjui.datagrid.child')

                if ($table && $table.length && $table.isTag('table'))
                    $table.datagrid('refresh', filterFlag)
            }

        if (row instanceof jQuery) {
            row.each(function () {
                refresh($(this))
            })
        } else if (isNaN(row)) {
            $.each(row.split(','), function (i, n) {
                if (n * 2 < $trs.length)
                    refresh($trs.eq(n * 2))
            })
        } else {
            if (row * 2 < $trs.length)
                refresh($trs.eq(row * 2))
        }
    }

    // api
    Datagrid.prototype.showChild = function (row, flag, func) {
        if (typeof flag === 'undefined')
            flag = 'toggle'

        var that = this, options = that.options, $child, $td, $table, index, trData, childOptions, dataUrl, $trs = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')'),
            fixedH = function () {
                //if (!options.fullGrid)
                that.fixedWidth()

                that.fixedHeight()
            },
            fixedL = function (index, showorhide, height) {
                if (that.$lockTableB) {
                    var $locktr = that.$lockTableB.find('> tbody > tr:eq(' + index + ')')

                    $locktr.toggle(showorhide)

                    if (height)
                        $locktr.height(height)
                    else if (!$locktr.height())
                        $locktr.height($child.height())
                }
            },
            show = function ($tr, $child, $table) {
                $td.find('> div').html(BJUI.doRegional(FRAG.gridShrinkBtn, that.regional))

                $child.fadeIn('normal', function () {
                    if (!$table.data('bjui.datagrid.init')) {
                        childOptions = $.extend(true, {}, that.childOptions), dataUrl = childOptions.dataUrl

                        if (that.isDom) trData = $tr.data('initData') || that.tools.setDomData($tr)
                        else {
                            trData = that.data[that.tools.getNoChildDataIndex($tr.index())]
                        }

                        if (dataUrl && !dataUrl.isFinishedTm()) {
                            dataUrl = that.tools.replacePlh(dataUrl, trData)

                            if (!dataUrl.isFinishedTm()) {
                                BJUI.debug('BJUI.Datagrid: The datagrid options \'childOptions\' in the \'dataUrl\' options is incorrect: ' + dataUrl)
                            } else {
                                childOptions.dataUrl = dataUrl
                            }
                        }

                        $table
                            .datagrid(childOptions)
                            .data('bjui.datagrid.parent', $tr)
                            .on('completed.bjui.datagrid', $.proxy(function () {
                                fixedH()
                                fixedL(index, true, $child.outerHeight())

                                if (func)
                                    func.apply(that, [$table])
                            }, that))
                            .on('bjui.datagrid.child.resize', function () {
                                $table
                                    .datagrid('fixedWidth')
                                    .datagrid('fixedHeight')
                            })

                        $tr.data('bjui.datagrid.child', $table)
                    } else {
                        fixedH()
                        fixedL(index, true)

                        if (func)
                            func.apply(that, [$table])
                    }
                })
            },
            hide = function ($tr, $child, $table) {
                $child.fadeOut('normal', function () {
                    $td.find('> div').html(BJUI.doRegional(FRAG.gridExpandBtn, that.regional))

                    fixedH()
                    fixedL(index, false)

                    if (func)
                        func.apply(that, [$table])
                })
            },
            showhide = function ($tr) {
                $child = $tr.next('.' + that.classnames.tr_child)

                if ($child && $child.length) {
                    $td = $tr.find('> td.' + that.classnames.td_child)
                    index = $child.index()
                    $table = $child.find('> td').find('table.table-child')

                    if ($table && $table.length) {
                        if (flag && flag === 'toggle') {
                            $child.is(':hidden') ? show($tr, $child, $table) : hide($tr, $child, $table)
                        } else {
                            flag ? show($tr, $child, $table) : hide($tr, $child, $table)
                        }
                    }
                }
            }

        if (func) {
            if (typeof func === 'string')
                func = func.toFunc()
            if (typeof func !== 'function')
                func = false
        }

        if (row instanceof jQuery) {
            row.each(function () {
                showhide($(this))
            })
        } else if (isNaN(row)) {
            $.each(row.split(','), function (i, n) {
                var tr = $trs.eq(parseInt(n.trim(), 10))

                if (tr && tr.length)
                    showhide(tr)
            })
        } else if (!isNaN(row)) {
            var tr = $trs.eq(row)

            if (tr && tr.length)
                showhide(tr)
        }
    }

    // api
    Datagrid.prototype.updateRow = function (row, updateData) {
        var that = this, options = that.options, data_index, data, url, $tr,
            doUpdate = function (tr, updateData) {
                $.extend(data, typeof updateData === 'object' && updateData)

                that.dialogEditComplete(tr, data)

                // refresh child
                if (data['refresh.datagrid.child']) {
                    var $child = tr.data('bjui.datagrid.child')

                    if ($child && $child.length)
                        $child.datagrid('refresh')
                    else
                        that.showChild(row)
                }

                // fixedH
                if (options.height === 'auto') {
                    var scrollTop = that.$boxB.scrollTop()

                    that.$boxB.scrollTop(5)
                    if (that.$boxB.scrollTop()) {
                        that.fixedHeight()
                        that.$boxB.scrollTop(scrollTop)
                    }
                }
            }

        if (row instanceof jQuery) {
            $tr = row
        } else if (!isNaN(row)) {
            $tr = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')').eq(parseInt(row, 10))
        } else {
            BJUI.debug('BJUI.Datagrid: Func \'updateRow\', Parameter \'row\' is incorrect!')
            return
        }

        if (that.isDom) data = $tr.data('initData') || that.tools.setDomData($tr)
        else {
            data_index = that.tools.getNoChildDataIndex($tr.index())
            data = that.data[data_index]
        }

        if (updateData) {
            if (typeof updateData === 'string') {
                if (updateData.trim().startsWith('{'))
                    updateData = updateData.toObj()
                else
                    updateData = updateData.toFunc()
            }
            if (typeof updateData === 'function')
                updateData = updateData.apply()

            if (typeof updateData !== 'object' && !options.updateRowUrl) {
                BJUI.debug('BJUI.Datagrid: Func \'updateRow\', Parameter \'updateData\' is incorrect!')
                return
            }

            doUpdate($tr, updateData)
        } else if (options.updateRowUrl) {
            url = that.tools.replacePlh(options.updateRowUrl, data)

            if (!url.isFinishedTm()) {
                BJUI.debug('BJUI.Datagrid: The datagrid options \'updateRowUrl\' is incorrect!')
            } else {
                BJUI.ajax('doajax', {
                    url: url,
                    type: options.loadType,
                    okCallback: function (json) {
                        doUpdate($tr, json)
                    }
                })
            }
        } else {
            BJUI.debug('BJUI.Datagrid: The datagrid options \'updateRowUrl\' is not set!')
        }
    }

    // api
    Datagrid.prototype.doAjaxRow = function (row, opts) {
        var that = this, options = that.options, $tr, data, data_index, url

        if (row instanceof jQuery) {
            $tr = row
        } else if (!isNaN(row)) {
            $tr = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')').eq(parseInt(row, 10))
        } else {
            BJUI.debug('BJUI.Datagrid: Func \'doAjaxRow\', Parameter \'row\' is incorrect!')
            return
        }
        if (typeof opts === 'object' && opts.url) {
            if (typeof opts.reload === 'undefined')
                opts.reload = false
            if (!opts.callback && !opts.okCallback) {
                opts.okCallback = function (json, options) {
                    that.updateRow($tr, typeof json === 'object' && !json[BJUI.keys.statusCode] && json)
                }
            }

            if (that.isDom) data = $tr.data('initData') || that.tools.setDomData($tr)
            else {
                data_index = that.tools.getNoChildDataIndex($tr.index())
                data = that.data[data_index]
            }

            url = opts.url

            if (!data || data.addFlag)
                url = url.replace(/{\/?[^}]*}/g, '')
            else
                url = that.tools.replacePlh(url, data)

            if (!url.isFinishedTm()) {
                BJUI.debug('BJUI.Datagrid: Func \'doAjaxRow\', options \'url\' is incorrect: ' + url)
            }

            opts.url = url
            opts.loadingmask = true
            opts.target = that.$boxB

            BJUI.ajax('doajax', opts)
        } else {
            BJUI.debug('BJUI.Datagrid: Func \'doAjaxRow\', options is incorrect or the property \'url\' is not set!')
        }
    }

    Datagrid.prototype.filter = function (data) {
        var that = this, options = that.options, nodatatr;
        if (that.$tbody) {
            nodatatr = that.$tbody.find('> tr.datagrid-nodata').length
        }
        if (data && typeof data === 'object') {
            if (data.clearOldPostData || !options.postData)
                options.postData = {}

            $.extend(options.postData, data)
        }

        // reset pageCurrent
        that.paging.pageCurrent = 1

        that.refresh()

        if (nodatatr) {
            that.$element.one('afterLoad.bjui.datagrid', function () {
                that.needfixedWidth = true
                that.fixedWidth()
                that.needfixedWidth = null
            })
        }
    }

    Datagrid.prototype.reload = function (option) {
        var that = this, options = that.options, data, $element

        if (option && typeof option === 'object') {
            if (option.clearOldPostData)
                delete options.postData

            if (option.columns)
                delete options.columns

            $.extend(true, options, option)
        }

        that.destroy()

        $element = that.$element
        $element.data('bjui.datagrid', (data = new Datagrid($element, options)))

        if (data)
            data.init()
    }

    Datagrid.prototype.destroy = function () {
        var that = this, $element = that.$grid.data('bjui.datagrid.table')

        if ($element) {
            that.$element
                .html($element.html())
                .removeData()
                .insertBefore(that.$grid)

            that.$grid.remove()
        }
    }

    Datagrid.prototype.initTop = function () {
        var that = this, options = that.options, regional = that.regional, hastoolbaritem = false, $group, groupHtml = '<div class="btn-group" role="group"></div>', btnHtml = '<button type="button" class="btn" data-icon=""></button>'

        if (options.showToolbar) {
            if (options.toolbarItem || options.toolbarCustom) {
                if (options.toolbarItem) {
                    var itemFunc = options.toolbarItem.toFunc()

                    if (typeof itemFunc === 'function') {
                        options.toolbarItem = itemFunc.apply()
                    }

                    hastoolbaritem = true
                    if (options.toolbarItem.indexOf('all') >= 0) options.toolbarItem = 'add, edit, cancel, save, |, del, |, refresh, |, import, export, exportf'
                    $.each(options.toolbarItem.split(','), function (i, n) {
                        n = n.trim().toLocaleLowerCase()
                        if (!$group || n === '|') {
                            $group = $(groupHtml).appendTo(that.$toolbar)
                            if (n === '|') return true
                        }

                        if (n === 'add') {
                            that.$toolbar_add = $(btnHtml).attr('data-icon', 'plus').addClass('btn-blue').text(options.addName || BJUI.getRegional('datagrid.add'))
                                .appendTo($group)
                                .on('click', function (e) {
                                    that.add()
                                })
                        } else if (n === 'edit') {
                            that.$toolbar_edit = $(btnHtml).attr('data-icon', 'edit').addClass('btn-green').text(options.editName || BJUI.getRegional('datagrid.edit'))
                                .appendTo($group)
                                .on('click', function (e) {
                                    var $selectTrs = that.$tbody.find('> tr.' + that.classnames.tr_selected)

                                    if (!options.editMode) return false
                                    if (!$selectTrs.length) {
                                        $(this).alertmsg('info', BJUI.getRegional('datagrid.selectMsg'))
                                    } else {
                                        if (options.inlineEditMult) {
                                            that.doEditRow($selectTrs)
                                        } else {
                                            if (that.$lastSelect) that.doEditRow(that.$lastSelect)
                                            else that.doEditRow($selectTrs.first())
                                        }
                                    }
                                })
                        } else if (n === 'cancel') {
                            that.$toolbar_calcel = $(btnHtml).attr('data-icon', 'undo').addClass('btn-orange').text(options.cancelName || BJUI.getRegional('datagrid.cancel'))
                                .appendTo($group)
                                .on('click', function (e) {
                                    that.doCancelEditRow(that.$tbody.find('> tr.' + that.classnames.tr_edit))
                                })
                        } else if (n === 'save') {
                            that.$toolbar_save = $(btnHtml).attr('data-icon', 'save').addClass('btn-default').text(options.saveName || BJUI.getRegional('datagrid.save'))
                                .appendTo($group)
                                .on('click', function (e) {
                                    that.doSaveEditRow()
                                })
                        } else if (n === 'del') {
                            that.$toolbar_del = $(btnHtml).attr('data-icon', 'times').addClass('btn-red').text(options.delName || BJUI.getRegional('datagrid.del'))
                                .appendTo($group)
                                .on('click', function (e) {
                                    var $selectTrs = that.$tbody.find('> tr.' + that.classnames.tr_selected)

                                    if ($selectTrs.length) {
                                        that.delRows($selectTrs)
                                    } else {
                                        $(this).alertmsg('info', BJUI.getRegional('datagrid.selectMsg'))
                                    }
                                })
                        } else if (n === 'refresh') {
                            that.$toolbar_refresh = $(btnHtml).attr('data-icon', 'refresh').addClass('btn-green').text(options.refreshName || BJUI.getRegional('datagrid.refresh'))
                                .appendTo($group)
                                .on('click', function (e) {
                                    that.refresh()
                                })
                        } else if (n === 'import') {
                            that.$toolbar_add = $(btnHtml).attr('data-icon', 'sign-in').addClass('btn-blue').text(options.importName || BJUI.getRegional('datagrid.import'))
                                .appendTo($group)
                                .on('click', function (e) {
                                    if (options.importOption) {
                                        var opts = options.importOption

                                        if (typeof opts === 'string')
                                            opts = opts.toObj()

                                        if (opts.options && opts.options.url) {
                                            if (opts.type === 'dialog') {
                                                that.$grid.dialog(opts.options)
                                            } else if (opts.type === 'navtab') {
                                                that.$grid.navtab(opts.options)
                                            } else {
                                                that.$grid.bjuiajax('doajax', opts.options)
                                            }
                                        }
                                    }
                                })
                        } else if (n === 'export') {
                            that.$toolbar_add = $(btnHtml).attr('data-icon', 'sign-out').addClass('btn-green').text(options.exportName || BJUI.getRegional('datagrid.export'))
                                .appendTo($group)
                                .on('click', function (e) {
                                    if (options.exportOption) {
                                        var opts = options.exportOption

                                        if (typeof opts === 'string')
                                            opts = opts.toObj()

                                        if (opts.options && opts.options.url) {
                                            if (!opts.options.data)
                                                opts.options.data = {}

                                            $.extend(opts.options.data, that.$element.data('filterDatas') || {}, that.sortData || {})
                                            opts.options.type = 'POST'

                                            if (opts.type === 'dialog') {
                                                BJUI.dialog(opts.options)
                                            } else if (opts.type === 'navtab') {
                                                BJUI.navtab(opts.options)
                                            } else if (opts.type === 'file') {
                                                opts.options.target = that.$boxB
                                                BJUI.ajax('ajaxdownload', opts.options)
                                            } else {
                                                BJUI.ajax('doajax', opts.options)
                                            }
                                        }
                                    }
                                })
                        } else if (n === 'exportf') {
                            that.$toolbar_add = $(btnHtml).attr('data-icon', 'filter').addClass('btn-green').text(options.exportfName || BJUI.getRegional('datagrid.exportf'))
                                .appendTo($group)
                                .on('click', function (e) {
                                    if (options.exportOption) {
                                        var opts = options.exportOption, filterDatas = that.tools.getRemoteFilterData(true)

                                        if (typeof opts === 'string')
                                            opts = opts.toObj()

                                        //导出若依据 form表单，则依据form表单的值进行过滤
                                        opts.options.data = new Object();
                                        if (opts.options.formId) {//表单过滤条件
                                            opts.options.data = $("#" + opts.options.formId).serializeArray();
                                        } else if (that.filterDatas) {//grid内置 过滤查询条件                                            
                                            var para = new Object();
                                            for (var key in that.filterDatas) {
                                                para[key] = that.filterDatas[key].datas.valA;
                                            }
                                            opts.options.data = para;
                                        }

                                        if (opts.options.outPutFileName) {
                                            opts.options.data.outPutFileName = opts.options.outPutFileName;
                                        }

                                        //导出的列头信息
                                        var mappingNames = new Array();
                                        var expIndex = 0;
                                        $(that.columnModel).each(function (i, item) {
                                            if (!item.hidden && item.name !== "gridCheckbox" && item.name !== "gridNumber" && item.name != undefined && item.name.length > 0 && mappingNames[item.name] == undefined) {
                                                var tempObj = new Object();
                                                tempObj.name = item.name;
                                                tempObj.title = item.label;
                                                tempObj.width = item.width || null;
                                                tempObj.align = item.align || null;

                                                //pattern：日期时间的格式化字符
                                                if (item.pattern) {
                                                    tempObj.pattern = item.pattern;
                                                }

                                                if (item.type) {
                                                    tempObj.type = item.type;
                                                }

                                                if (item.type === 'select') {
                                                    if (item.items) {
                                                        tempObj.items = item.items;
                                                    }

                                                    if (item.itemattr) {
                                                        tempObj.itemattr = item.itemattr;
                                                    }
                                                }
                                                mappingNames[expIndex] = tempObj;
                                                expIndex++;
                                            }
                                        });
                                        opts.options.data.mappingNames = JSON.stringify(mappingNames);
                                        if (opts.options && opts.options.url) {
                                            if (!opts.options.data)
                                                opts.options.data = {}

                                            $.extend(opts.options.data, filterDatas, that.sortData || {})

                                            opts.options.type = 'POST'

                                            if (opts.type === 'dialog') {
                                                BJUI.dialog(opts.options)
                                            } else if (opts.type === 'navtab') {
                                                BJUI.navtab(opts.options)
                                            } else if (opts.type === 'file') {
                                                opts.options.target = that.$boxB
                                                BJUI.ajax('ajaxdownload', opts.options)
                                            } else {
                                                BJUI.ajax('doajax', opts.options)
                                            }
                                        }
                                    }
                                })
                        }
                    })
                }

                if (options.toolbarCustom) {
                    var $custom, $custombox = $('<div style="display:inline-block;"></div>')

                    if (typeof options.toolbarCustom === 'string') {
                        var custom = $(options.toolbarCustom)

                        if (custom && custom.length) {
                            $custom = custom
                        } else {
                            custom = custom.toFunc()
                            if (custom) {
                                $custom = custom.call(that)
                                if (typeof $custom === 'string') $custom = $($custom)
                            }
                        }
                    } else if (typeof options.toolbarCustom === 'function') {
                        $custom = options.toolbarCustom.call(that)
                        if (typeof $custom === 'string') $custom = $($custom)
                    } else {
                        $custom = options.toolbarCustom
                    }

                    if ($custom && $custom.length && typeof $custom !== 'string') {
                        if (hastoolbaritem) {
                            $custombox.css('margin-left', '5px')
                        }
                        $custombox.appendTo(that.$toolbar)
                        $custom.appendTo($custombox)
                    }
                }

                that.$toolbar.initui()
            }
        }
    }

    Datagrid.prototype.initThead = function () {
        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel, width, cols = []

        that.$tableH.append(that.$thead)

        that.init_thead = true
        that.$trsH = that.$thead.find('> tr')
        that.table_width = 0
        that.$colgroupH = that.$tableH.find('> colgroup')
        that.fixtedColumnWidthCount = 0

        $.each(that.columnModel, function (i, n) {
            var lockWidth = ''

            if (n === that.checkboxColumn || n === that.childColumn || n === that.editBtnsColumn || n.finalWidth)
                lockWidth = ' class="datagrid_col_lockwidth"'

            width = n.width

            if ((!width || width === 'auto') && !that.hasAutoCol) {
                that.hasAutoCol = true
            }

            cols.push('<col style="width:' + (width && width !== 'auto' ? width + 'px' : 'auto') + '"' + lockWidth + '>')
            n.width = width
        })

        that.table_width = that.$grid.width()

        that.$colgroupH.html(cols.join(''))

        // thead - events
        var $ths = that.$trsH.find('> th')
        // events - quicksort
        $ths.filter('.datagrid-quicksort-th')
            .on('click.bjui.datagrid.quicksort', function (e) {
                var $target = $(e.target)

                if (!$target.closest('.' + that.classnames.th_cell).length && !that.isResize)
                    tools.quickSort(columnModel[$(this).data('index')])
            })

        // events - filter
        $ths.filter('.datagrid-column-menu')
            .on('filter.bjui.datagrid.th', function (e, flag) {
                var $th = $(this), $btn = $th.find('> div > .' + that.classnames.th_cell + '> .' + that.classnames.btn_menu + '> .btn')

                if (flag) {
                    $th.addClass('filter-active')
                    $btn.find('> i').attr('class', 'fa fa-filter')
                } else {
                    $th.removeClass('filter-active')
                    $btn.find('> i').attr('class', 'fa fa-bars')
                }
            })

        // events - contextmenu
        if (options.contextMenuH) {
            tools.contextmenuH()
        }

        that.initTbody()

        if (options.columnResize) that.colResize()
        if (options.columnMenu) that.colMenu()
        if (options.paging) that.initPaging()
        if (options.editMode) that.edit()

        var delayFunc = function () {
            if (options.showTfoot) that.initTfoot()

            /* render */
            if (that.renderTds && that.renderTds.length) {
                var $trs = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ', .datagrid-nodata)'), j = that.renderTds && that.renderTds.length, fixedWidthModel = {}
                var $tempTrs = $(), tempDatas = []

                for (var i = 0; i < j; i++) {
                    var obj = that.renderTds[i], label = obj.render.call(that, obj.label, obj.data, that.columnModel[obj.tdindex].items, that.columnModel[obj.tdindex].itemattr)
                    var tempData = $.extend({}, obj.data)

                    if (!fixedWidthModel[obj.tdindex] && that.columnModel[obj.tdindex].fixedWidth)
                        fixedWidthModel[obj.tdindex] = that.columnModel[obj.tdindex]

                    if (that.isTemplate) {
                        var tempData = {}, tempIndex = $.inArray($trs.eq(obj.trindex)[0], $tempTrs)

                        if (tempIndex == -1) {
                            $tempTrs = $tempTrs.add($trs.eq(obj.trindex))

                            tempData = $.extend({}, obj.data)
                            tempData[that.columnModel[obj.tdindex]['name']] = label

                            tempDatas.push(tempData)
                        } else {
                            tempDatas[tempIndex][that.columnModel[obj.tdindex]['name']] = label
                        }
                    } else {
                        $trs.eq(obj.trindex).find('> td:eq(' + obj.tdindex + ') > div:last').html(label)
                    }
                }

                // for tdTemplate
                $.each($tempTrs, function (i) {
                    var $td = $(this).find('> td.datagrid-template-td'), html = $td.html()

                    if ($td.length) {
                        $td.html(tools.replacePlh4Template(html, tempDatas[i]))
                    }
                })
            }

            if (that.isTemplate)
                that.$grid.data('bjui.datagrid.trs.template', that.$tbody.html())
            else
                that.$grid.data('bjui.datagrid.trs.normal', that.$tbody.html())

            that.initEvents()
            that.$tableB.initui()
            that.$lockTableB && that.$lockTableB.initui()

            tools.setBoxbH()

            /* tree - collapse */
            if (that.collapseIndex && that.collapseIndex.length) {
                that.expand(that.collapseIndex.join(','), false)
                that.collapseIndex = null
            }

            setTimeout(function () {
                that.fixedWidth('init')
                that.initLock()
                that.$element.trigger('completed.bjui.datagrid', { datas: that.data })
                that.$boxM && that.$boxM.trigger('bjui.ajaxStop').hide()

                if ((that.options.width.endsWith('%') &&
                    (that.options.flowLayoutWidth && that.$grid.width() < that.options.flowLayoutWidth)
                    || that.options.dialogFilterW && that.$grid.width() < that.options.dialogFilterW)
                    || that.options.dialogFilterW === 0) {
                    $(window).resize()
                }

                that.resizeGrid()
            }, 50)

            that.delayFilterTimeout && clearInterval(that.delayFilterTimeout)
        }

        if (options.filterThead) {
            if (that.delayRender) {
                that.delayFilterTimeout = setInterval(function () {
                    if (!that.delayRender) {
                        that.filterInThead()
                        delayFunc()
                    }
                }, 100)
            } else {
                that.filterInThead()
                delayFunc()
            }
        } else {
            delayFunc()
        }
    }

    Datagrid.prototype.fixedWidth = function (isInit) {
        var that = this, options = that.options, bW, excludeW = 0, fixedW, columnModel = that.columnModel, length = columnModel.length

        if (isInit && that.initFixedW) return
        that.initFixedW = true

        var setNewWidth = function () {
            if (String(that.options.width).endsWith('%'))
                that.$grid.css('width', '')

            that.$boxH.find('> div').css('width', '')
            that.$boxB.find('> div').css('width', '')
            that.$boxF && that.$boxF.find('> div').css('width', '')
            that.$boxP && that.$boxP.find('> div.paging-content').css('width', '')

            bW = (that.$boxB.find('> div'))[0].clientWidth

            if (that.options.hasChild) {
                var scrollTop = that.$boxB.scrollTop()

                that.$boxB.scrollTop(1)

                if (that.$boxB.scrollTop()) {
                    that.$boxB.scrollTop(scrollTop)
                }
            }
            if (that.$element.hasClass('table-child'))
                bW = bW - 18

            that.$boxB.find('> div').width(bW)
            that.$boxH.find('> div').width(bW)
            that.$boxF && that.$boxF.find('> div').width(bW)

            if (that.table_width > bW || options.fullGrid)
                that.$boxP && that.$boxP.find('> div.paging-content').width(bW)
            else
                that.$boxP && that.$boxP.find('> div.paging-content').width(that.table_width)

            if (options.fullGrid || that.isTemplate || that.needfixedWidth || (that.hasAutoCol && isInit)) {
                $('body').find('> .bjui-datagrid').remove()

                var oldTheight = that.$tableH.height(),
                    $grid = $('<div class="bjui-datagrid forwidth"></div>'),
                    $tableB = that.$element.clone().addClass('table table-bordered').appendTo($grid)

                $tableB.find('> tbody').before(that.$thead.clone())

                $tableB.find('> colgroup > col').each(function (i) {
                    var $col = $(this), dataLength = that.data.length ? String(that.data[that.data.length - 1].gridNumber).length : 2

                    if ($col.hasClass('datagrid_col_lockwidth')) return true
                    if (columnModel[i] === that.linenumberColumn) {
                        dataLength == 1 && (dataLength = 2)
                        $col.width(dataLength * 15)
                    } else if (!columnModel[i].width || columnModel[i].width === 'auto' || that.isTemplate)
                        $col.width('')
                })

                $grid.width(bW + 17).height(that.$boxB.height()).prependTo($('body'))

                if (!(options.fullGrid || that.isTemplate) && options.hScrollbar) {
                    $grid.width('')
                }

                if (that.isTemplate)
                    options.tableWidth = '100%'

                $tableB.attr('style', 'width:' + (options.tableWidth || 'auto') + ' !important;')
                $tableB.find('thead th > div').height('')

                var $tr = $tableB.find('> tbody > tr:first')

                if (!$tr.length || that.isTemplate || $tr.hasClass('datagrid-nodata')) {
                    var trHtml = BJUI.StrBuilder()

                    trHtml.add('<tr>')

                    for (var i = 0, j = $tableB.find('colgroup > col').length; i < j; i++) {
                        if ($($tableB.find('colgroup > col')[i]).css("display") !== "none") {
                            trHtml.add('<td></td>')
                        }
                    }

                    trHtml.add('</tr>')

                    $tableB.find('> tbody').prepend(trHtml.toString())
                    $tr = $tableB.find('> tbody > tr:first')
                }

                if (that.isTemplate) {
                    that.$boxP && that.$boxP.find('> div.paging-content').width('100%')
                }

                var $ths = that.$thead.find('th')

                $tableB.find('thead > tr:not(.datagrid-filter) > th').each(function (i) {
                    var $this = $(this), v = $this.is(':hidden')

                    if (v) $this.show()
                    $ths.eq(i).find('> div').height($this.find('> div').height())
                    if (v) $this.hide()
                })

                that.$boxB.height(that.$boxB.height() + (oldTheight - $tableB.find('> thead').height()))
                that.$boxM && that.$boxM.height(that.$boxB.height()).css('top', that.$boxB.position().top)
                that.$lockB && that.$lockB.height(that.$boxB.height())

                $tr.find('> td').each(function (i) {
                    var $col = that.$colgroupB.find('> col:eq(' + i + ')')

                    !($col.hasClass('datagrid_col_lockwidth')) && $col.width($(this).outerWidth())
                })

                that.$colgroupH.html(that.$colgroupB.html())

                if (!that.options.noremove)
                    $grid.remove()
            }
        }

        setNewWidth()
    }

    Datagrid.prototype.fixedHeight = function (height) {
        var that = this, options = that.options

        if (options.height === 'auto') {
            that.boxH = 'auto'
            that.$grid.css('height', '')
            that.$boxB.css('height', '')
            that.tools.setBoxbH()
        } else
            that.tools.setBoxbH(height)

        // if scrollLeft
        var scrollLeft = that.$boxB.scrollLeft()

        that.$boxB.scrollLeft(5)

        if (that.$boxB.scrollLeft()) {
            that.fixedWidth()
            that.$boxB.scrollLeft(scrollLeft)
        }
    }

    Datagrid.prototype.initTbody = function () {
        var that = this, options = that.options, tools = that.tools, $trs = that.$tbody.find('> tr'), $tds = $trs.find('> td'), width = options.tableWidth || '0'

        that.init_tbody = true
        that.$colgroupB = that.$colgroupH.clone()

        that.$tableB.prepend(that.$colgroupB)

        if (options.fullGrid || that.isTemplate) width = '100%'

        that.$tableH.css('width', width)
        that.$tableB.css('width', width)

        // add class
        that.$tableB.removeAttr('data-toggle width').addClass('table table-bordered').removeClass('table-hover')

        that.$boxB
            .scroll(function () {
                that.$boxH.find('> div').prop('scrollLeft', this.scrollLeft)
                that.$boxF && that.$boxF.find('> div').prop('scrollLeft', this.scrollLeft)
                that.$lockB && that.$lockB.prop('scrollTop', this.scrollTop)
            })

        // if DOM to datagrid
        if (that.isDom) {
            if (options.showLinenumber) {
                that.showLinenumber(options.showLinenumber)
            }
            if (options.showCheckboxcol) {
                that.showCheckboxcol(options.showCheckboxcol)
            }
            if (options.showEditbtnscol) {
                that.showEditCol(options.showEditbtnscol)
            }

            that.$grid.data(that.datanames.tbody, that.$tbody.clone())
        }
    }

    // init events(only tbody)
    Datagrid.prototype.initEvents = function ($trs) {
        var that = this, options = that.options, trs = that.$tbody.find('> tr')

        if (!$trs) $trs = trs

        $trs.on('click.bjui.datagrid.tr', function (e, checkbox) {
            var $tr = $(this), index = $tr.index(), data, $selectedTrs = that.$tbody.find('> tr.' + that.classnames.tr_selected), $last = that.$lastSelect, checked, $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr')

            if (checkbox) {
                checked = checkbox.is(':checked')
                if (!checked) that.$lastSelect = $tr
                that.selectedRows($tr, !checked)
            } else {
                if ($tr.hasClass(that.classnames.tr_edit)) return
                if (options.selectMult) {
                    that.selectedRows($tr)
                } else {
                    if (!BJUI.KeyPressed.ctrl && !BJUI.KeyPressed.shift) {
                        if ($selectedTrs.length > 1 && $tr.hasClass(that.classnames.tr_selected)) {
                            that.selectedRows($selectedTrs.not($tr))
                            that.$lastSelect = $tr
                        } else {
                            if ($selectedTrs.length && $selectedTrs[0] != this) that.selectedRows(null)
                            if (!$tr.hasClass(that.classnames.tr_selected)) that.$lastSelect = $tr
                            that.selectedRows($tr)
                        }
                    } else {
                        //window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty() //clear selection

                        if (BJUI.KeyPressed.ctrl) {
                            if (!$tr.hasClass(that.classnames.tr_selected)) that.$lastSelect = $tr
                            that.selectedRows($tr)
                        } else if (BJUI.KeyPressed.shift) {
                            if (!$last) $last = that.$lastSelect = $tr
                            if ($last.length) {
                                that.selectedRows(null)
                                if ($last.index() != index) {
                                    if ($last.index() > index) {
                                        that.selectedRows($tr.nextUntil($last).add($tr).add($last), true)
                                    } else {
                                        that.selectedRows($tr.prevUntil($last).add($tr).add($last), true)
                                    }
                                } else {
                                    that.selectedRows(index)
                                }
                            }
                        }
                    }
                }
            }

            if (that.isDom)
                data = that.tools.setDomData($tr)
            else
                data = that.data[that.tools.getNoChildDataIndex(index)]

            that.$element.trigger('clicked.bjui.datagrid.tr', { target: e.target, tr: $tr, data: data })
        })
            .on('mouseenter.bjui.datagrid', function (e) {
                var $tr = $(this), index = $tr.index()

                if (that.isDrag) return false

                $tr.addClass('datagrid-hover')
                that.$lockTbody && that.$lockTbody.find('> tr:eq(' + index + ')').addClass('datagrid-hover')
            })
            .on('mouseleave.bjui.datagrid', function (e) {
                var $tr = $(this), index = $tr.index()

                if (that.isDrag) return false

                $tr.removeClass('datagrid-hover')
                that.$lockTbody && that.$lockTbody.find('> tr:eq(' + index + ')').removeClass('datagrid-hover')
            })
            // custom event - delete 
            .on('delete.bjui.datagrid.tr', function (e) {
                e.stopPropagation()

                var $tr = $(this), tr_index = $tr.index(), data_index = tr_index, data = that.data, gridIndex, allData = that.allData, $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr')

                if ($tr.hasClass('datagrid-nodata')) return false

                if (that.options.hasChild && that.options.childOptions) {
                    data_index = data_index / 2

                    // remove child dom
                    $tr.next().remove()
                    $lockTrs && $lockTrs.eq(tr_index).next().remove()
                }

                /* remove tree - child */
                if (that.options.isTree) {
                    var $childrens = that.getChildrens($tr, null, true), len = $childrens.length

                    if (that.options.hasChild && that.options.childOptions)
                        len = len / 2

                    $childrens.remove()

                    if (!that.isDom) {
                        gridIndex = data[data_index].gridIndex

                        that.data.splice(data_index + 1, len)
                        that.allData.splice(gridIndex + 1, len)
                    }
                }

                if (!that.isDom) {
                    gridIndex = data[data_index].gridIndex
                    that.data = data.remove(data_index)     // remove data in the current page data
                    that.allData = allData.remove(gridIndex)   // remove data in allData

                    that.tools.updateGridIndex()
                }

                /* update gridNumber */
                if ($.inArray(that.linenumberColumn, that.columnModel) != -1) {
                    $tr.nextAll(':not(.' + that.classnames.tr_child + ')').each(function () {
                        var $td = $(this).find('> td.' + that.classnames.td_linenumber), num = parseInt($td.text(), 10)

                        $td.text(num - 1)
                    })

                    $lockTrs && $lockTrs.eq(tr_index).trigger('delete.bjui.datagrid.tr', [tr_index])
                }

                // remove dom
                $tr.remove()
                $lockTrs && $lockTrs.eq(tr_index).remove()

                // no data
                that.tools.createNoDataTr()
            })

        // child
        that.$grid.off('click.bjui.datagrid.tr.child').on('click.bjui.datagrid.tr.child', 'td.' + that.classnames.td_child, function (e) {
            e.stopPropagation()

            var $this = $(this), $tr = $this.closest('tr'), $child = $tr.next('.' + that.classnames.tr_child)

            if ($child && $child.length)
                that.showChild($tr, !$child.is(':visible'))
        })
            // td checkbox
            .off('ifClicked').on('ifClicked', 'td.' + that.classnames.td_checkbox + ' input', function (e) {
                e.stopPropagation()

                var $this = $(this), $tr = $this.closest('tr'), tr_index = $tr.index()

                that.$tbody.find('> tr:eq(' + tr_index + ')').trigger('click.bjui.datagrid.tr', [$this])
            })
            // th checkbox - check all
            .off('ifChanged').on('ifChanged', 'th.' + that.classnames.td_checkbox + ' input', function (e) {
                e.stopPropagation()

                var checked = $(this).is(':checked'), $trs = that.$tbody.find('> tr:not(".' + that.classnames.tr_child + '")')

                that.selectedRows($trs, checked)
            })


        //contextmenu
        if (options.contextMenuB) {
            $trs.each(function () {
                that.tools.contextmenuB($(this))
            })
        }

        // custom update
        that.$tableB.off('click.bjui.datagrid.refresh').on('click.bjui.datagrid.refresh', 'tbody > tr > td [data-toggle="update.datagrid.tr"]', function (e) {
            e.stopPropagation()
            e.preventDefault()

            that.updateRow($(this).closest('tr'))
        })
            // custom edit
            .off('click.bjui.datagrid.edit').on('click.bjui.datagrid.edit', 'tbody > tr > td [data-toggle="edit.datagrid.tr"]', function (e) {
                e.stopPropagation()
                e.preventDefault()

                var $this = $(this), opts = $this.data('options'), $tr = $this.closest('tr')

                if (opts && typeof opts === 'string') opts = opts.toObj()

                if (typeof opts === 'object')
                    that.externalEdit($tr, opts)
                else
                    that.doEditRow($tr)
            })
            // custom ajax
            .off('click.bjui.datagrid.ajax').on('click.bjui.datagrid.ajax', 'tbody > tr > td [data-toggle="ajax.datagrid.tr"]', function (e) {
                e.stopPropagation()
                e.preventDefault()

                var $this = $(this), opts = $this.data()

                if (opts.options && typeof opts.options === 'string') opts.options = opts.options.toObj()
                $.extend(opts, typeof opts.options === 'object' && opts.options)

                if (!opts.url && $this.attr('href'))
                    opts.url = $this.attr('href')

                that.doAjaxRow($this.closest('tr'), opts)
            })
            // custom addChild
            .off('click.bjui.datagrid.addchild').on('click.bjui.datagrid.addchild', 'tbody > tr > td [data-toggle="addchild.datagrid.tr"]', function (e) {
                e.stopPropagation()
                e.preventDefault()

                var $this = $(this), $tr = $this.closest('tr'), $table = $tr.data('bjui.datagrid.child'), $child = $tr.next('.' + that.classnames.tr_child), trData, data = $this.data('addData'), addData,
                    replaceData = function (data) {
                        return data.replace(/#\/?[^#]*#/g, function ($1) {
                            var key = $1.replace(/[##]+/g, ''), val = trData[key]

                            if (typeof val === 'undefined' || val === 'null' || val === null)
                                val = ''

                            return val
                        })
                    },
                    doAdd = function ($table) {
                        if (data) {
                            if (typeof data === 'string') {
                                data = replaceData(data)

                                if (data.trim().startsWith('{'))
                                    data = data.toObj()
                                else
                                    data = data.toFunc()
                            } else {
                                if (typeof data === 'function') {
                                    data = data.toFunc()
                                }
                                if (typeof data === 'object') {
                                    data = JSON.stringify(data)
                                }

                                data = replaceData(data).toObj()
                            }

                            if (typeof data === 'object') {
                                addData = data
                            }
                        }

                        $table.datagrid('add', false, addData)
                    }

                if ($child && $child.length) {
                    if (that.isDom) trData = $tr.data('initData') || tools.setDomData($tr)
                    else trData = that.data[that.tools.getNoChildDataIndex($tr.index())]

                    if ($table && $table.length) {
                        doAdd($table)
                    } else {
                        that.showChild($tr, true, doAdd)
                    }
                }
            })
            // custom delete
            .off('click.bjui.datagrid.del').on('click.bjui.datagrid.del', 'tbody > tr > td [data-toggle="del.datagrid.tr"]', function (e) {
                e.stopPropagation()
                e.preventDefault()

                that.delRows($(this).closest('tr'))
            })
            // tree expand || collapse
            .off('click.bjui.datagrid.tree').on('click.bjui.datagrid.tree', 'tbody > tr > td .datagrid-tree-switch', function (e) {
                e.stopPropagation()
                e.preventDefault()

                var $t = $(this), $tr = $t.closest('tr'), childLen = $tr.data('child'), level = $tr.data('level'), isExpand = !$t.hasClass('collapsed')

                if (childLen) {
                    $tr.data('isExpand', !isExpand)
                        .nextAll('.datagrid-tree-level-' + (level + 1)).slice(0, childLen).toggleClass('collapsed', isExpand).trigger('switch.child.bjui.datagrid.tree', { isExpand: isExpand })
                    $t.toggleClass('collapsed', isExpand).find('> i').attr('class', 'fa ' + (isExpand ? 'fa-plus-square-o' : 'fa-minus-square-o'))
                }

                if (options.height === 'auto') {
                    that.fixedHeight()
                }
            })
            .off('switch.child.bjui.datagrid.tree').on('switch.child.bjui.datagrid.tree', 'tbody > tr', function (e, data) {
                e.stopPropagation()
                e.preventDefault()

                var $tr = $(this), childLen = $tr.data('child'), level = $tr.data('level')

                if (childLen) {
                    $tr.nextAll('.datagrid-tree-level-' + (level + 1)).slice(0, childLen)
                        .toggleClass('collapsed-child', data[that.options.treeOptions.isExpand])
                        .trigger('switch.child.bjui.datagrid.tree', { isExpand: data[that.options.treeOptions.isExpand] })
                }
            })
            .off('expand.bjui.datagrid.tree').on('expand.bjui.datagrid.tree', 'tbody > tr', function (e) {
                e.stopPropagation()
                e.preventDefault()

                var $tr = $(this).data('isExpand', true), $t = $tr.find('> td.datagrid-tree-td .datagrid-tree-switch')

                if (!$t || !$t.length || !$t.hasClass('collapsed')) return false

                $t.trigger('click.bjui.datagrid.tree')

                if (options.height === 'auto') {
                    that.fixedHeight()
                }
            })
            .off('collapse.bjui.datagrid.tree').on('collapse.bjui.datagrid.tree', 'tbody > tr', function (e) {
                e.stopPropagation()
                e.preventDefault()

                var $tr = $(this).data('isExpand', false), $t = $tr.find('> td.datagrid-tree-td .datagrid-tree-switch')

                if (!$t || !$t.length || $t.hasClass('collapsed')) return false

                $t.trigger('click.bjui.datagrid.tree')

                if (options.height === 'auto') {
                    that.fixedHeight()
                }
            })
            // tree add btn
            .off('mouseover.bjui.datagrid.tree').on('mouseover.bjui.datagrid.tree', 'tbody > tr > td.datagrid-tree-td', function (e) {
                e.stopPropagation()
                e.preventDefault()

                if (!options.treeOptions.add || that.isDrag)
                    return false

                var $td = $(this), $tr = $td.closest('tr'), $add = $td.find('.datagrid-tree-add')

                if (!$add || !$add.length)
                    $td.find('> div').append('<a href="javascript:;" class="datagrid-tree-add" data-toggle="add.datagrid.tree" title="添加"><i class="fa fa-plus-circle"></i></a>')
                else
                    $add.show()
            })
            .off('mouseout.bjui.datagrid.tree').on('mouseout.bjui.datagrid.tree', 'tbody > tr > td.datagrid-tree-td', function (e) {
                e.stopPropagation()
                e.preventDefault()

                if (!options.treeOptions.add || that.isDrag)
                    return false

                var $td = $(this), $add = $td.find('.datagrid-tree-add')

                if ($add && $add.length)
                    $add.hide()
            })
            // tree add
            .off('click.bjui.datagrid.tree.add').on('click.bjui.datagrid.tree.add', 'tbody > tr > td.datagrid-tree-td [data-toggle="add.datagrid.tree"]', function (e) {
                e.stopPropagation()
                e.preventDefault()

                var $add = $(this), $tr = $add.closest('tr'), opts = $add.data()

                if (opts.addData) {
                    if (typeof opts.addData === 'string')
                        opts.addData = opts.addData.toObj()
                }

                that.addTree($tr, opts.addLocation || '', opts.addData || {})
            })

        if (options.dropOptions.drop) {
            var $trs = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')'), beforeDrag = options.dropOptions.beforeDrag

            if (typeof beforeDrag === 'string')
                beforeDrag = beforeDrag.toFunc()

            if (typeof beforeDrag !== 'function')
                beforeDrag = false

            $trs
                .basedrag('destroy')
                .basedrag({
                    exclude: 'input, button, a, .bjui-lookup, .datagrid-tree-switch, .datagrid-tree-add, .datagrid-child-td',
                    drop: $trs,
                    container: that.$boxB,
                    beforeDrag: beforeDrag,
                    treeData: that.data,
                    drag: function () {
                        that.isDrag = true
                    },
                    stop: function () {
                        that.isDrag = false

                        $(this).find('> td.datagrid-tree-td').trigger('mouseout.bjui.datagrid.tree')
                    }
                })
                .off('dropover.bjui.basedrag').on('dropover.bjui.basedrag', function (e, x, y, target) {
                    trs.removeClass('datagrid-hover datagrid-drop-over-after datagrid-drop-over-before').find('.datagrid-tree-box').removeClass('datagrid-drop-over-append')

                    if (this === target[0] || target.hasClass(that.classnames.tr_edit)) {
                        return false
                    }
                    if (that.options.isTree) {
                        var $childrens = that.getChildrens(target)

                        if ($.inArray(this, $childrens) !== -1)
                            return false
                    }

                    var $this = $(this).addClass('datagrid-hover'), height = $this.outerHeight() / 2, top = $this.offset().top

                    if (that.options.isTree)
                        height = 5

                    if (y < (top + height)) {
                        $this.addClass('datagrid-drop-over-before').prevAll(':visible:first').addClass('datagrid-drop-over-after')
                    } else {
                        if (!that.options.isTree || (that.options.isTree && y > (top + $this.outerHeight() - height)))
                            $this.addClass('datagrid-drop-over-after')
                        else if (that.options.isTree) {
                            $this.find('.datagrid-tree-box').addClass('datagrid-drop-over-append')
                        }
                    }
                })
                .off('dropout.bjui.basedrag').on('dropout.bjui.basedrag', function (e, data) {
                    $trs.removeClass('datagrid-hover datagrid-drop-over-after datagrid-drop-over-before')
                    $trs.find('div.datagrid-drop-over-append').removeClass('datagrid-drop-over-append')
                })
                .off('drop.bjui.basedrag').on('drop.bjui.basedrag', function (e, target) {
                    var $this = $(this), position, beforeDrop = options.dropOptions.beforeDrop, afterDrop = options.dropOptions.afterDrop

                    that.afterDrop = false

                    if ($this.hasClass('datagrid-drop-over-before'))
                        position = 'top'
                    else if ($this.hasClass('datagrid-drop-over-after'))
                        position = 'bottom'
                    else if (that.options.isTree && $this.find('.datagrid-tree-box').hasClass('datagrid-drop-over-append'))
                        position = 'append'

                    if (position) {
                        if (position != 'append')
                            that.insertTr(target, $this, position)
                        else {
                            var $parents = that.getParents(target, null, true)

                            if ($.inArray(this, $parents) !== -1) {
                                $this.find('.datagrid-tree-box').removeClass('datagrid-drop-over-append')

                                return false
                            }

                            that.appendTr(target, $this)
                        }
                    }

                    $trs.removeClass('datagrid-drop-over-after datagrid-drop-over-before').find('.datagrid-tree-box').removeClass('datagrid-drop-over-append')

                    if (afterDrop && that.afterDrop) {
                        if (typeof afterDrop === 'function') {
                            afterDrop.apply(that, [target, $this])
                        } else if (options.dropOptions.dropUrl) {
                            var postData = { pageSize: that.paging.pageSize, pageCurrent: that.paging.pageCurrent }, type = options.editType, opts = { url: options.dropOptions.dropUrl, type: 'POST', reload: false }

                            postData['list'] = []

                            $.each(that.data, function (i, data) {
                                var _data = $.extend({}, data)

                                if (options.dropOptions.scope !== 'drop' || data[options.keys.dropFlag]) {
                                    for (var key in options.keys)
                                        delete _data[options.keys[key]]
                                }
                                if (options.dropOptions.scope === 'drop') {
                                    if (data[options.keys.dropFlag]) {
                                        postData['list'].push(_data)
                                    }
                                } else {
                                    postData['list'].push(_data)
                                }
                            })

                            if (postData) {
                                postData = JSON.stringify(options.dropOptions.paging && options.paging ? postData : postData['list'])

                                if (type && type === 'raw') {
                                    opts.data = postData
                                    opts.contentType = 'application/json'
                                } else {
                                    opts.data = { json: postData }
                                    type && (opts.type = type)
                                }

                                BJUI.ajax('doajax', opts)
                            }
                        }
                    }
                })
        }
    }

    Datagrid.prototype.getChildrens = function (tr, childrens, hasChild, maxLevel) {
        var that = this, childLen = tr.data('child'), level = tr.data('level')

        if (maxLevel) maxLevel--
        if (!childrens) childrens = $()
        if (childLen) {
            tr.nextAll('.datagrid-tree-level-' + (level + 1)).slice(0, childLen).each(function () {
                childrens = childrens.add(this)

                if (hasChild && $(this).next().hasClass(that.classnames.tr_child))
                    childrens = childrens.add($(this).next())

                if (maxLevel)
                    childrens = childrens.add(that.getChildrens($(this), childrens, hasChild, maxLevel))
            })
        }

        return childrens
    }

    Datagrid.prototype.getParents = function (tr, parents, onlyParent, hasChild) {
        var that = this, level = tr.data('level'), prev

        if (!parents) parents = $()
        if (level) {
            prev = tr.prevAll('.datagrid-tree-level-' + (level - 1) + ':first')
            if (prev.length) {
                parents = parents.add(prev)

                if (hasChild && prev.next().hasClass(that.classnames.tr_child))
                    parents = parents.add(prev.next())

                if (!onlyParent)
                    parents = parents.add(that.getParents(prev, parents, hasChild))
            }
        }

        return parents
    }

    // only for tree
    Datagrid.prototype.appendTr = function (sTr, dTr, option) {
        if (!this.options.isTree)
            return

        var that = this, options = that.options, sData, dData, sIndex = that.tools.getNoChildDataIndex(sTr.index()), dIndex = that.tools.getNoChildDataIndex(dTr.index()), hasChild = sTr.next().hasClass(that.classnames.tr_child),
            keys = options.treeOptions.keys,
            sParent, sParentData, sLevel = sTr.data('level'), dLevel = (dTr.data('level') + 1) || 0, dChildLen = dTr.data('child') + 1,
            dParent, dTd = dTr.find('> td.datagrid-tree-td'), moveIndex, len,
            _lastDTr
        var beforeDrop = options.dropOptions.beforeDrop

        if (hasChild) {
            sTr = sTr.add(sTr.next())
            dTr = dTr.add(dTr.next())
        }

        sData = that.data[sIndex]
        dData = that.data[dIndex]
        /* beforeDrop */
        if (beforeDrop && typeof beforeDrop === 'string')
            beforeDrop = beforeDrop.toFunc()
        if (beforeDrop && typeof beforeDrop === 'function') {
            if (!beforeDrop.apply(this, [sData, dData, 'append'])) {
                return
            }
        }

        sParent = that.getParents(sTr, null, true)
        if (sParent.length) {
            sParentData = that.data[that.tools.getNoChildDataIndex(sParent.index())]

            sParentData[keys.childLen] = sParentData[keys.childLen] - 1
            sParent.data('child', sParentData[keys.childLen]).attr('data-child', sParentData[keys.childLen])

            if (!sParentData[keys.childLen]) {
                delete sParentData[keys.isParent]
                delete sParentData[keys.isExpand]

                var sParentTd = sParent.find('> td.datagrid-tree-td')

                sParentTd.html(that.tools.createTreePlaceholder(sParentData, sParentTd.find('.datagrid-tree-title').html()), sParent.data('isExpand'))
            }
        }

        if (dTr.length) {
            dTr = that.getChildrens(dTr, dTr, true)
            _lastDTr = that.getChildrens(dTr.last(), dTr.last(), true, true).last()
            //dData = that.data[dIndex]
            that.expand(dTr.first(), true)

            dData[keys.isParent] = true
            dData[keys.childLen] = dChildLen

            dTr.first().data('child', dChildLen).attr('data-child', dChildLen)
            dTd.html(that.tools.createTreePlaceholder(dData, dTd.find('.datagrid-tree-title').html(), dTr.first().data('isExpand')))
        }

        sTr = that.getChildrens(sTr, sTr, true)
        sTr.filter(':not(.' + that.classnames.tr_child + ')').each(function () {
            var $tr = $(this), level = $tr.data('level'), newLevel = level + (dLevel - sLevel), $td = $tr.find('> td.datagrid-tree-td'), label = $td.find('.datagrid-tree-title').html(),
                trIndex = $tr.index(), trData = that.data[that.tools.getNoChildDataIndex(trIndex)]

            if (option && option.isUp) {
                newLevel = dData[keys.level]
            }
            trData[keys.level] = newLevel

            $tr.removeClass('datagrid-tree-level-' + level).addClass('datagrid-tree-level-' + newLevel)
                .data('level', newLevel).attr('data-level', newLevel)

            $td.html(that.tools.createTreePlaceholder(trData, label, $tr.data('isExpand')))
        })

        if (option && option.dIndex)
            moveIndex = option.dIndex
        else {
            moveIndex = dTr.last().index() + 1
            if (hasChild)
                moveIndex = moveIndex / 2
        }

        //sData = that.data[sIndex]
        len = (hasChild ? sTr.length / 2 : sTr.length)

        that.data.moveItems(sIndex, moveIndex, len)
        that.allData.moveItems(sData.gridIndex, (sData.gridIndex + (moveIndex - sIndex)), len)

        that.tools.updateGridIndex()

        if (option && option._dTr) {
            dParent = that.getParents(option._dTr, null, true, false)
            if (option.isBefore)
                sTr.insertBefore(option._dTr)
            else if (option.isUp) {
                sTr.insertAfter(_lastDTr)
            } else {
                sTr.insertAfter(option._dTr)
            }
        } else {
            sTr.insertAfter(_lastDTr)
        }

        // update the source seq
        if (sParent) {
            var _childs = that.getChildrens(sParent, null, false)

            _childs.each(function (i) {
                var _trData = that.data[that.tools.getNoChildDataIndex($(this).index())]

                if (_trData) {
                    _trData[keys.order] = i
                    _trData[options.keys.dropFlag] = true
                }
            })
        }

        // update this seq and parentid
        if (!dParent)
            dParent = dTr.first()

        if (dParent && dParent.length) {
            var _childs = that.getChildrens(dParent, null, false), dParentData = that.data[that.tools.getNoChildDataIndex(dParent.index())]

            sData[keys.parentKey] = dParentData[keys.key]

            _childs.each(function (i) {
                var _trData = that.data[that.tools.getNoChildDataIndex($(this).index())]

                if (_trData) {
                    _trData[keys.order] = i
                    _trData[options.keys.dropFlag] = true
                }
            })
        } else {
            var _trs = sTr.siblings('.datagrid-tree-level-' + sData[keys.level]).addBack()

            sData[keys.parentKey] = null

            _trs.each(function (i) {
                var _trData = that.data[that.tools.getNoChildDataIndex($(this).index())]

                if (_trData) {
                    _trData[keys.order] = i
                    _trData[options.keys.dropFlag] = true
                }
            })
        }

        that.tools.updateLinenumber()

        that.afterDrop = true
    }

    Datagrid.prototype.insertTr = function (sTr, dTr, position) {
        var that = this, point, options = that.options, keys = options.treeOptions.keys, sData, dData, moveIndex,
            sIndex = that.tools.getNoChildDataIndex(sTr.index()), dIndex = that.tools.getNoChildDataIndex(dTr.index()), hasChild = sTr.next().hasClass(that.classnames.tr_child),
            _start, _end
        var beforeDrop = options.dropOptions.beforeDrop
        var insertTr = function (sTr, dTr, point) {
            if (position === 'bottom') {
                if (hasChild)
                    dTr = dTr.next()
                sTr.insertAfter(dTr)
            } else {
                sTr.insertBefore(dTr)
            }
        }

        sData = that.data[sIndex]
        dData = that.data[dIndex]

        /* beforeDrop */
        if (beforeDrop && typeof beforeDrop === 'string')
            beforeDrop = beforeDrop.toFunc()
        if (beforeDrop && typeof beforeDrop === 'function') {
            if (!beforeDrop.apply(this, [sData, dData, position])) {
                return
            }
        }

        if (sIndex > dIndex) {
            point = position === 'top' ? 0 : 1
        } else {
            point = position === 'top' ? -1 : 0
        }

        moveIndex = dIndex + point

        if (moveIndex == sIndex && !options.isTree) return

        if (hasChild)
            sTr = sTr.add(sTr.next())

        if (that.isDom) {
            insertTr(sTr, dTr, point)
        } else {
            sData = that.data[sIndex]
            dData = that.data[dIndex]

            if (options.isTree) {
                var len, option = {}, append = false, sParent = that.getParents(sTr.first(), null, true), dParent = that.getParents(dTr, null, true)

                if (dData[keys.level] === sData[keys.level] && dData[keys.order] == sData[keys.order] + (position === 'bottom' ? -1 : 1)) {
                    return
                }

                /* to append */
                // if bottom
                if (position === 'bottom' && dData[keys.isParent]) {
                    append = true
                    option._dTr = dTr
                    option.dIndex = dIndex + 1
                    option.isUp = true
                }
                // if different level or not the same parent
                else if (sTr.data('level') != dTr.data('level') || sParent[0] !== dParent[0]) {
                    append = true
                    option._dTr = dTr
                    option.dIndex = moveIndex
                    dTr = dParent

                    if (position === 'top')
                        option.isBefore = true

                    if (sIndex < dIndex) {
                        option.dIndex++
                    }
                }
                if (append) {
                    that.appendTr(sTr, dTr, option)

                    return
                }

                sTr = that.getChildrens(sTr, sTr, true)
                len = (hasChild ? sTr.length / 2 : sTr.length)

                that.data.moveItems(sIndex, moveIndex, len)
                that.allData.moveItems(sData.gridIndex, (dData.gridIndex + point), len)

                _start = (sIndex > dIndex ? dIndex : sIndex)
                _end = (sIndex > dIndex ? sIndex : dIndex) + (len - 1)
            } else {
                that.data.move(sIndex, moveIndex)
                that.allData.move(sData.gridIndex, (dData.gridIndex + point))

                _start = sIndex > dIndex ? dIndex : sIndex
                _end = sIndex > dIndex ? sIndex : dIndex
            }

            that.tools.updateGridIndex()

            insertTr(sTr, dTr, point)

            // update the seqs
            if (sParent) {
                var _childs = that.getChildrens(sParent, null, false, 1)

                _childs.each(function (i) {
                    var _trData = that.data[that.tools.getNoChildDataIndex($(this).index())]

                    if (_trData && (_trData[options.keys.gridIndex] >= _start && _trData[options.keys.gridIndex] <= _end)) {
                        _trData[keys.order] = i
                        _trData[options.keys.dropFlag] = true
                    }
                })
            } else {
                for (var i = _start; i <= _end; i++) {
                    var _trData = that.data[i]

                    _trData[options.keys.dropFlag] = true
                }
            }

            that.tools.updateLinenumber()

            that.afterDrop = true
        }
    }

    Datagrid.prototype.addTree = function (row, addLocation, addData) {
        var that = this, options = that.options, $tr, data, tr_index, index

        if (options.isTree) {
            if (!addData || typeof addData !== 'object') {
                addData = {}
            }

            if (row instanceof jQuery) {
                $tr = row.first()
            } else {
                $tr = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')').eq(parseInt(row, 10))
            }

            if ($tr && $tr.length) {
                that.$lastSelect = $tr
                that.add(addLocation, addData)
            }
        }
    }

    Datagrid.prototype.expand = function (rows, expandFlag) {
        var that = this, options = that.options, $trs = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')'), event

        if (typeof expandFlag === 'undefined') expandFlag = true

        event = (expandFlag ? 'expand' : 'collapse') + '.bjui.datagrid.tree'

        if (options.isTree) {
            if (rows instanceof jQuery) {
                rows.trigger(event)
            } else if (isNaN(rows)) {
                $.each(rows.split(','), function (i, n) {
                    var tr = $trs.eq(parseInt(n.trim(), 10))

                    if (tr && tr.length)
                        tr.trigger(event)
                })
            } else if (!isNaN(rows)) {
                var tr = $trs.eq(rows)

                if (tr && tr.length)
                    tr.trigger(event)
            }
        }
    }

    Datagrid.prototype.initTfoot = function () {
        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel, $tr = $('<tr></tr>')

        that.$boxF = $('<div class="datagrid-box-f"></div>')
        that.$colgroupF = that.$colgroupH.clone()
        that.$tableF = that.$tableH.clone().empty()
        that.$tableF.append(that.$colgroupF)
        that.$boxF.append($('<div class="datagrid-wrap-h"></div>').append(that.$tableF))
        that.$boxF.insertAfter(that.$boxB)

        that.$tfoot = $('<thead></thead>')
        $.each(columnModel, function (i, n) {
            var $th = $('<th><div></div></th>')

            if (n.calc) {
                var calc_html = '<div><div class="datagrid-calcbox">#tit#</div>#number#</div>'

                if (n.calc === 'avg')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'AVG')).replace('#number#', (n.calc_sum / n.calc_count).toFixed(n.calcDecimal || 2)))
                else if (n.calc === 'count')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'COUNT')).replace('#number#', (n.calc_count)))
                else if (n.calc === 'sum')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'SUM')).replace('#number#', (n.calc_sum)))
                else if (n.calc === 'max')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'MAX')).replace('#number#', (n.calc_max)))
                else if (n.calc === 'min')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'MIN')).replace('#number#', (n.calc_min)))
            }

            if (n.hidden) $th.css('display', 'none')

            $th.appendTo($tr)
        })

        that.$tfoot.append($tr).appendTo(that.$tableF)

        // events
        that.$tfoot.on('resizeH.bjui.datagrid.tfoot', function () {
            tools.setBoxbH(options.height)
        })
    }

    // selected row
    Datagrid.prototype.selectedRows = function (rows, selected) {
        var that = this, $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr'), $trs = that.$tbody.find('> tr')
        var selectedTr = function (n) {
            if (typeof selected === 'undefined') selected = true
            if ($trs.eq(n).hasClass(that.classnames.tr_child)) return

            $trs.eq(n)
                .toggleClass(that.classnames.tr_selected, selected)
                .find('> td.' + that.classnames.td_checkbox + ' input').iCheck(selected ? 'check' : 'uncheck')

            $lockTrs && $lockTrs.eq(n)
                .toggleClass(that.classnames.tr_selected, selected)
                .find('> td.' + that.classnames.td_checkbox + ' input').iCheck(selected ? 'check' : 'uncheck')
        }

        if (rows === null) {
            $trs.removeClass(that.classnames.tr_selected)
                .find('> td.' + that.classnames.td_checkbox + ' input').iCheck('uncheck')

            $lockTrs && $lockTrs.removeClass(that.classnames.tr_selected)
                .find('> td.' + that.classnames.td_checkbox + ' input').iCheck('uncheck')
        } else if (typeof rows === 'object') {
            rows.each(function () {
                var $row = $(this), index = $row.index()

                if ($row.hasClass(that.classnames.tr_child)) return true

                if (typeof selected !== 'undefined') {
                    selectedTr(index)
                } else {
                    $row.toggleClass(that.classnames.tr_selected)
                        .trigger('bjui.datagrid.tr.selected')
                        .find('> td.' + that.classnames.td_checkbox + ' input').iCheck(($row.hasClass(that.classnames.tr_selected) ? 'check' : 'uncheck'))

                    $lockTrs && $lockTrs.eq(index)
                        .toggleClass(that.classnames.tr_selected)
                        .trigger('bjui.datagrid.tr.selected')
                        .find('> td.' + that.classnames.td_checkbox + ' input').iCheck(($row.hasClass(that.classnames.tr_selected) ? 'check' : 'uncheck'))
                }
            })
        } else if (isNaN(rows)) {
            $.each(rows.split(','), function (i, n) {
                selectedTr(parseInt(n.trim(), 10))
            })
        } else if (!isNaN(rows)) {
            selectedTr(parseInt(rows, 10))
        }

        if (that.$lastSelect && !that.$lastSelect.hasClass(that.classnames.tr_selected)) {
            that.$lastSelect = null
        }

        // selectedTrs
        var $selectedTrs = that.$tbody.find('> tr.' + that.classnames.tr_selected), datas = []

        $selectedTrs.each(function () {
            var $tr = $(this), data_index = $tr.index(), data

            data_index = that.tools.getNoChildDataIndex(data_index)

            if (that.isDom) data = $tr.data('initData') || that.tools.setDomData($tr)
            else data = that.data[data_index]

            datas.push(data)
        })

        that.$element.data('selectedTrs', $selectedTrs).data('selectedDatas', datas)
    }

    //lock
    Datagrid.prototype.initLock = function () {
        var that = this, columnModel = that.columnModel

        that.col_lock_count = 0
        $.each(that.columnModel, function (i, n) {
            if (n.initLock) that.col_lock_count++
        })

        if (that.col_lock_count) that.doLock()
    }

    //api - colLock
    Datagrid.prototype.colLock = function (column, lockFlag) {
        var that = this, $th, index, columnModel

        if (that.isTemplate)
            return
        if ($.type(column) === 'number') {
            index = parseInt(column, 10)
            if (index < 0 || index > that.columnModel.length - 1) return
            columnModel = that.columnModel[index]
            $th = columnModel.th
        } else {
            $th = column
            index = $th.data('index')
            columnModel = that.columnModel[index]
        }

        if (columnModel === that.editBtnsColumn) return // edit btn column
        else if (columnModel.index === that.columnModel.length - 1) return // last column
        if (typeof columnModel.locked === 'undefined') columnModel.locked = false
        if (columnModel.locked === lockFlag) return

        columnModel.initLock = lockFlag

        if (lockFlag) {
            that.col_lock_count++
        } else {
            that.col_lock_count--
        }
        if (that.col_lock_count < 0) that.col_lock_count = 0

        that.doLock()
    }

    Datagrid.prototype.fixedLockItem = function (type) {
        var that = this, columnModel = that.columnModel, $filterThs = that.$thead.find('> tr.datagrid-filter > th'), $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr')

        // out
        if (!type) {
            var fixedTh = function ($th, $lockTh) {
                $lockTh.clone().insertAfter($lockTh)
                $lockTh.hide().insertAfter($th)
                $th.remove()
            }

            // filterThead
            if ($filterThs && $filterThs.length) {
                that.$lockThead && that.$lockThead.find('> tr.datagrid-filter > th:visible').each(function () {
                    var $lockTh = $(this), index = $lockTh.index(), $th = $filterThs.eq(index)

                    fixedTh($th, $lockTh)
                })
            }

            //thead checkbox
            if (that.$lockThead) {
                // checkbox
                if ($.inArray(that.checkboxColumn, columnModel) != -1 && that.checkboxColumn.locked) {
                    var $lockTh = that.$lockThead.find('> tr:first > th.' + that.classnames.td_checkbox), index = that.checkboxColumn.index, $th = that.$thead.find('> tr:first > th:eq(' + index + ')')

                    fixedTh($th, $lockTh)
                }
            }

            // inline edit && checkbox td
            if ($lockTrs && $lockTrs.length) {
                $lockTrs.each(function () {
                    var $lockTr = $(this), tr_index = $lockTr.index(), $tr, $td,
                        fixedTd = function ($lockTd, tr_index, td_index) {
                            $tr = that.$tbody.find('> tr:eq(' + tr_index + ')')
                            $td = $tr.find('> td:eq(' + td_index + ')')
                            $lockTd.clone().insertAfter($lockTd)
                            $lockTd.hide().insertAfter($td)
                            $td.remove()
                        }

                    if ($lockTr.hasClass(that.classnames.tr_edit)) {
                        var $lockTd = $lockTr.find('> td:eq(' + columnModel.lockIndex + ')'), td_index = $lockTd.index()

                        if ($lockTd.hasClass(that.classnames.td_edit)) {
                            fixedTd($lockTd, tr_index, td_index)
                        }
                    }

                    if ($.inArray(that.checkboxColumn, columnModel) != -1 && that.checkboxColumn.locked) {
                        $lockTr.find('> td.' + that.classnames.td_checkbox).each(function () {
                            var $lockTd = $(this), td_index = that.checkboxColumn.index

                            fixedTd($lockTd, tr_index, td_index)
                        })
                    }
                })
            }
        } else { //in
            var fixedTh = function ($th, $lockTh) {
                $th.clone().html('').insertAfter($th)
                $th.show().insertAfter($lockTh)
                $lockTh.remove()
            }

            // filterThead
            if ($filterThs && $filterThs.length) {
                that.$lockThead.find('> tr.datagrid-filter > th:visible').each(function () {
                    var $lockTh = $(this), index = $lockTh.index(), $th = $filterThs.eq(index)

                    fixedTh($th, $lockTh)
                })
            }

            //thead checkbox
            if (that.$lockThead) {
                // checkbox
                if ($.inArray(that.checkboxColumn, columnModel) != -1 && that.checkboxColumn.locked) {
                    var $lockTh = that.$lockThead.find('> tr:first > th.' + that.classnames.td_checkbox), index = that.checkboxColumn.index, $th = that.$thead.find('> tr:first > th:eq(' + index + ')')

                    fixedTh($th, $lockTh)
                }
            }

            // inline edit && checkbox td
            if ($lockTrs && $lockTrs.length) {
                $lockTrs.each(function () {
                    var $lockTr = $(this), tr_index = $lockTr.index(), $tr, $td,
                        fixedTd = function ($lockTd, tr_index, td_index) {
                            $tr = that.$tbody.find('> tr:eq(' + tr_index + ')')
                            $td = $tr.find('> td:eq(' + td_index + ')')
                            $td.clone().insertAfter($td)
                            $td.show().insertAfter($lockTd)
                            $lockTd.remove()
                        }

                    if ($lockTr.hasClass(that.classnames.tr_edit)) {
                        $lockTr.find('> td.' + that.classnames.td_edit + ':visible').each(function () {
                            var $lockTd = $(this), td_index = $lockTd.index(), model = columnModel[td_index]

                            if (model.locked) {
                                fixedTd($lockTd, tr_index, td_index)
                            }
                        })
                    }
                    if ($.inArray(that.checkboxColumn, columnModel) != -1 && that.checkboxColumn.locked) {
                        $lockTr.find('> td.' + that.classnames.td_checkbox).each(function () {
                            var $lockTd = $(this), td_index = that.checkboxColumn.index

                            fixedTd($lockTd, tr_index, td_index)
                        })
                    }
                })
            }
        }
    }

    //locking
    Datagrid.prototype.doLock = function () {
        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel, tableW = that.$tableH.width(), width = 0, $trs, $lockTrs, lockedLen = 0
        var hasFoot = that.$boxF && true, top = 0

        if (!that.$lockBox || !that.$lockBox.length) {
            that.$lockBox = $('<div class="datagrid-box-l"></div>')
            that.$lockH = $('<div class="datagrid-box-h"></div>')
            that.$lockB = $('<div class="datagrid-box-b"></div>')
            that.$lockF = $('<div class="datagrid-box-f"></div>')

            that.$lockTableH = $('<table class="table table-bordered"></table>')
            that.$lockTableB = $('<table></table>').addClass(that.$tableB.attr('class'))
            that.$lockTableF = $('<table class="table table-bordered"></table>')

            that.$lockH.append(that.$lockTableH)
            that.$lockB.append(that.$lockTableB)
            that.$lockF.append(that.$lockTableF)

            that.$lockBox.append(that.$lockH).append(that.$lockB).prependTo(that.$grid)
            if (hasFoot) {
                that.$lockBox.append(that.$lockF)
                that.$lockF.css('margin-top', (that.$boxB.outerHeight() - that.$boxB[0].clientHeight)).height(that.$boxF.outerHeight())
            }
        } else {
            that.fixedLockItem()
            that.$lockTableH.empty()
            that.$lockTableB.empty()
            that.$lockTableF && that.$lockTableF.empty()
        }

        if (that.$boxT) top += that.$boxT.outerHeight()
        if (that.$toolbar) top += that.$toolbar.outerHeight()
        if (top) that.$lockBox.css({ top: top })

        // display initLock columns, hide other
        $.each(columnModel, function (i, n) {
            n.lockShow = false
            n.lockHide = false
            if (n.initLock) {
                if (n.hidden) tools.showhide(n, true)
                n.lockHide = true
                n.locked = true
                n.lockIndex = lockedLen++
                width += n.width
            } else {
                n.lockShow = true
                if (!n.hidden) tools.showhide(n, false)
                else n.lockShow = false
                if (n.locked) n.lockShow = true
                n.locked = false
            }
        })

        that.$lockThead = that.$thead.clone(true)
        that.$lockTbody = that.$tbody.clone()
        that.$lockColgroupH = that.$colgroupH.clone()
        that.$lockColgroupB = that.$colgroupB.clone()

        that.$lockTableH.append(that.$lockColgroupH).append(that.$lockThead).css('width', width)
        that.$lockTableB.append(that.$lockColgroupB).append(that.$lockTbody).css('width', width)

        if (hasFoot) {
            that.$lockTfoot = that.$tableF.find('> thead').clone()
            that.$lockColgroupF = that.$colgroupF.clone()
            that.$lockTableF.append(that.$lockColgroupF).append(that.$lockTfoot).css('width', width)
        }

        // display unlock columns, hide locked columns
        $.each(that.columnModel, function (i, n) {
            if (n.lockShow) tools.showhide(n, true)
            if (n.lockHide) tools.showhide(n, false)
        })

        that.$boxH.find('> div').css('width', '')
        that.$boxB.find('> div').css('width', '')
        that.$boxF && that.$boxF.find('> div').css('width', '')

        setTimeout(function () {
            var bw = that.$boxB.find('> div').width()

            that.$boxB.find('> div').width(bw)
            that.$boxH.find('> div').width(bw)
            that.$boxF && that.$boxF.find('> div').width(bw)
        }, 50)

        if (!that.col_lock_count) that.$lockBox.hide()
        else that.$lockBox.show()

        // colspan for child tr && nodata tr
        that.$tbody.find('> .' + that.classnames.tr_child + ', .datagrid-nodata').each(function () {
            $(this).find('> td').attr('colspan', that.columnModel.length - that.col_lock_count)
        })

        if (width > 1) width = width - 1
        that.$boxH.css('margin-left', width)
        that.$boxB.css('margin-left', width)
        if (hasFoot) that.$boxF.css('margin-left', width)

        // fixed height
        that.$lockB.height(that.$boxB[0].clientHeight)
        that.$lockB.prop('scrollTop', that.$boxB[0].scrollTop)

        var lockH = that.$lockTableH.height(), H = that.$thead.height(), lockFH = that.$lockTableF && that.$lockTableF.height(), HF = that.$tableF && that.$tableF.height()

        if (lockH != H) {
            if (lockH < H) that.$lockTableH.height(H)
            else that.$tableH.height(lockH)
        }

        if (lockFH && HF && (lockFH != HF)) {
            if (lockFH < HF) that.$lockTableF.find('> thead > tr:first-child > th:visible:first').height(HF)
            else that.$tableF.find('> thead > tr:first-child > th:visible:first').height(lockFH)
        }

        $lockTrs = that.$lockTbody.find('> tr')
        $trs = that.$tbody.find('> tr')

        setTimeout(function () {
            var lockBH = that.$lockTableB.height(), HB = that.$tableB.height()

            if (lockBH != HB) {
                if (lockBH > HB) {
                    $lockTrs.each(function (tr_index) {
                        var $lockTr = $(this), $lockTd = $lockTr.find('> td:visible:first'), newH = $lockTd.outerHeight()

                        if (newH > 30) {
                            $lockTr.height(newH)
                            $trs.eq(tr_index).height(newH)
                        }
                    })
                } else {
                    $trs.each(function (tr_index) {
                        var $tr = $(this), $td = $tr.find('> td:visible:first'), newH = $td.outerHeight()

                        if (newH > 30) {
                            $tr.height(newH)
                            $lockTrs.eq(tr_index).height(newH)
                        }
                    })
                }
            }
        }, 20)

        that.fixedLockItem(1)

        // remove hidden tds
        $lockTrs.find('> td:hidden').remove()

        // events
        that.initLockEvents($lockTrs)
    }

    // init lockTr Events
    Datagrid.prototype.initLockEvents = function ($locktrs) {
        if (!this.$lockTbody) return

        var that = this, options = that.options

        if (!$locktrs) $locktrs = that.$lockTbody.find('> tr')

        $locktrs
            .on('click.bjui.datagrid.tr', function (e) {
                var index = $(this).index(), $td = $(e.target).closest('td')

                that.$tbody.find('> tr:eq(' + index + ')').trigger('click.bjui.datagrid.tr')
            })
            .on('delete.bjui.datagrid.tr', function (e, index) {
                var $tr = $(this)

                if (that.linenumberColumn && that.linenumberColumn.locked) {
                    $tr.nextAll(':not(.' + that.classnames.tr_child + ')').each(function () {
                        var $td = $(this).find('> td.' + that.classnames.td_linenumber), num = parseInt($td.text(), 10)

                        $td.text(num - 1)
                    })
                }
            })
            .on('mouseenter.bjui.datagrid', function (e) {
                var $tr = $(this), index = $tr.index()

                $tr.addClass('datagrid-hover')
                that.$tbody.find('> tr:eq(' + index + ')').addClass('datagrid-hover')
            })
            .on('mouseleave.bjui.datagrid', function (e) {
                var $tr = $(this), index = $tr.index()

                $tr.removeClass('datagrid-hover')
                that.$tbody.find('> tr:eq(' + index + ')').removeClass('datagrid-hover')
            })

        //contextmenu
        if (options.contextMenuB) {
            $locktrs.each(function () {
                that.tools.contextmenuB($(this), true)
            })
        }
    }

    //api - filterInThead
    Datagrid.prototype.filterInThead = function (isDialog) {
        var that = this, options = that.options, tools = that.tools, regional = that.regional, columnModel = that.columnModel, filterData = {}
        var $tr = isDialog ? $('<ul class="datagrid-thead-dialog-view-ul"></ul>') : $('<tr class="datagrid-filter"></tr>')
        var onFilter = function ($input, model, $th) {
            var type = model.type, $others

            if ($th.isTag('th')) {
                $others = that.$headFilterUl && that.$headFilterUl.find('> li.li-' + model.index)
            } else if ($th.isTag('li')) {
                $others = that.$thead.find('> tr.datagrid-filter > th:eq(' + model.index + ')')
            }

            if ($others) {
                $input.val($others.find('input').val())
                if ($input.isTag('select'))
                    $input.val($others.find('select').val()).selectpicker('refresh')
            }

            // 开启 filterThead 后，输入查询数据后，回车 即可 自动查询
            if (type === 'date') {
                $input.change(function () {
                    doFilter(model, $input.val())
                })
            } else if (type === 'findgrid') {
                $input.change(function () {
                    doFilter(model, $input.val())
                })
            } else if (type === 'tags') {
                $th.on('aftercreated.bjui.tags', '[data-toggle="tags"]', function (e, data) {
                    doFilter(model, data.value)
                })
            } else if (type === 'select') {
                $input.change(function () {
                    doFilter(model, $input.val())
                })
            }
            else {

                $input.on('keyup', function (e) {
                    if (e.which === BJUI.keyCode.ENTER) {
                        doFilter(model, $input.val())
                    }
                });
            }

            $input.change(function () {
                if ($th.isTag('th') && !$others) {
                    $others = that.$headFilterUl && that.$headFilterUl.find('> li.li-' + model.index)
                }

                $others && $others.find('input').val($input.val())
                if ($input.isTag('select'))
                    $others && $others.find('select').val($input.val()).selectpicker('refresh')
            })
        }
        var doFilter = function (model, val) {
            //tools.quickFilter(model, val ? { operatorA: 'like', valA: val } : null)
            tools.quickFilter(model, val ? { operatorA: (model.operator ? model.operator : 'like'), valA: val } : null)
        }
        var initFilter = (typeof options.initFilter === 'object') && options.initFilter

        if (!that.inputs || !that.inputs.length) tools.initEditInputs()

        $.each(columnModel, function (i, n) {
            if (isDialog) {
                if (n === that.linenumberColumn || n === that.checkboxColumn || n === that.editBtnsColumn || n === that.childColumn)
                    return true
            }

            var $input = $(that.inputs[i]), $th = isDialog ? $('<li></li>') : $('<th></th>'), attrs = ''

            if (!n.quickfilter) {
                $th.appendTo($tr)
                return true
            }
            else if (n.type === 'findgrid') $input.data('context', $tr)
            else if (n.type === 'spinner') $input = $('<input type="text" name="' + n.name + '">')
            else if (n.type === 'boolean') $input = $(BJUI.doRegional('<select name="' + n.name + '" data-toggle="selectpicker"><option value="">#all#</option><option value="true">#true#</option><option value="false">#false#</option></select>', regional))
            else if (n.type === 'select') {
                if (n.attrs && typeof n.attrs === 'object') {
                    $.each(n.attrs, function (i, n) {
                        attrs += ' ' + i + '=' + n
                    })
                }
                if ($input.find('> option:first-child').val() && (!n.attrs || !n.attrs.multiple)) {
                    $input = $('<select name="' + n.name + '" data-toggle="selectpicker"' + attrs + '></select>')
                        .append(BJUI.doRegional('<option value="">#all#</option>', regional))
                        .append($input.html())

                    $input.val('') // for IE8
                }
            }

            if (isDialog)
                $th.data('index', i).addClass('li-' + i).append('<a href="javascript:;" class="datagrid-thead-dialog-sort" title="' + BJUI.getRegional('datagrid.asc') + '/' + BJUI.getRegional('datagrid.desc') + '">' + n.label + '</a>')

            $th.append($input)

            if (n.hidden) $th.hide()
            if (n.type === 'boolean' && !isDialog) $th.attr('align', 'center')
            $th.appendTo($tr)

            $input.data('clearFilter', false)

            onFilter($input, n, $th)

            // events - clear filter
            $input.on('clearfilter.bjui.datagrid.thead', function () {
                $input.val('')
                if (n.type === 'boolean' || n.type === 'select') $input.selectpicker('refresh')
            })

            n.$quickfilter = $input

            // init filter
            if (initFilter && typeof initFilter[n.name] !== 'undefined') {
                that.tools.initFilter($input, n)
            }
        })

        if (isDialog) {
            $tr.prepend('<li><label>' + BJUI.getRegional('datagrid.asc') + '/' + BJUI.getRegional('datagrid.desc') + '</label><span class="quicksort-title">' + BJUI.getRegional('datagrid.filter') + '</span></li>')
            $tr.hide().appendTo(that.$grid).initui()

            that.$headFilterUl = $tr
            // for thead
            that.sortModels = !that.sortModels ? [] : that.sortModels

            $tr.off('click.datagrid.thead.quicksort').on('click.datagrid.thead.quicksort', 'a.datagrid-thead-dialog-sort', function (e) {
                var $this = $(this), model = that.columnModel[$this.closest('li').data('index')], html = []

                $this.find('> i').remove()

                that.tools.quickSort(model)

                if (model.sortDesc) {
                    $this.append('<i class="fa fa-long-arrow-down"></i>')
                    that.sortModels[model.index] = model
                } else if (model.sortAsc) {
                    $this.append('<i class="fa fa-long-arrow-up"></i>')
                    that.sortModels[model.index] = model
                } else {
                    delete that.sortModels[model.index]
                }

                if (!that.sortData || $.isEmptyObject(that.sortData))
                    that.sortModels = []

                $.each(that.sortModels, function (i, n) {
                    if (n)
                        html.push('<i class="datagrid-sort-i fa fa-long-arrow-' + (n.sortAsc ? 'up' : 'down') + '"></i> ' + n.label)
                })

                that.$boxH.find('.datagrid-thead-dialog-filter-msg > .msg-sort')
                    .html(html.length > 0 ? html.join('<em>&nbsp;&gt;&nbsp;</em>') : '')
            })
        } else
            $tr.appendTo(that.$thead).initui()
    }

    //api - showhide linenumber column
    Datagrid.prototype.showLinenumber = function (flag) {
        var that = this, options = that.options, model = that.columnModel, numberModel, modelOrder = -1, data = that.data, numberModel_index = $.inArray(that.linenumberColumn, model)

        if (numberModel_index != -1)
            numberModel = model[numberModel_index]

        if (numberModel) {
            if (typeof flag === 'string' && (flag === 'lock' || flag === 'unlock')) {
                that.colLock(numberModel.th, flag === 'lock' ? true : false)
            } else {
                that.showhideColumn(numberModel.th, flag ? true : false)
            }
        } else if (flag) {
            modelOrder = $.inArray(that.childColumn, model)

            modelOrder++
            numberModel = that.linenumberColumn
            numberModel.index = modelOrder
            model.splice(modelOrder, 0, numberModel)

            if (that.inputs && that.inputs.length)
                that.inputs = that.inputs.splice(modelOrder, 0, '')

            var $trsH = that.$thead.find('> tr'), col = '<col style="width:30px;">', $th, $filterTr = $trsH.filter('.datagrid-filter'), rowspan = $trsH.length - $filterTr.length

            $th = $('<th align="center"' + (rowspan > 1 ? ' rowspan="' + rowspan + ' "' : '') + 'class="' + that.classnames.td_linenumber + (rowspan == 1 ? ' single-row' : '') + '"><div><div class="datagrid-space"></div><div class="datagrid-label">' + that.linenumberColumn.label + '</div></div></th>')

            that.$colgroupH.find('> col:eq(' + modelOrder + ')').before(col)
            that.$colgroupB.find('> col:eq(' + modelOrder + ')').before(col)
            that.$colgroupF && that.$colgroupF.find('> col:eq(' + modelOrder + ')').before(col)
            $filterTr.length && $filterTr.find('> th:eq(' + modelOrder + ')').before('<th></th>')
            $trsH.first().find('> th:eq(' + modelOrder + ')').before($th)
            that.$tableF && that.$tableF.find('> thead > tr > th:eq(' + modelOrder + ')').before('<th></th>')

            numberModel.th = $th
            numberModel.width = 30

            that.$tbody.find('> tr').each(function (i) {
                var $tr = $(this), colspan = that.columnModel.length, linenumber = i, paging = that.paging

                if ($tr.hasClass(that.classnames.tr_child) || $tr.hasClass('datagrid-nodata'))
                    $tr.find('> td').attr('colspan', colspan)
                else {
                    linenumber = that.tools.getNoChildDataIndex(linenumber)
                    if (options.linenumberAll)
                        linenumber = ((paging.pageCurrent - 1) * paging.pageSize + (linenumber))

                    $tr.find('> td:eq(' + modelOrder + ')').before('<td align="center" class="' + that.classnames.td_linenumber + '">' + (linenumber + 1) + '</td>')
                }
            })

            that.$tableF && that.$tableF.find('> thead > tr > th:eq(' + modelOrder + ')').before('<th></th>')

            $.each(model, function (i, n) {
                n.index = i
                if (n.th) n.th.data('index', i)
            })

            $th.find('> div').height($th.outerHeight())

            if (flag === 'lock') {
                that.colLock($th, true)
            }
            if (that.$showhide) {
                that.$showhide.remove()
                that.colShowhide(options.columnShowhide)
            }
        }
    }

    //api - showhide checkbox column
    Datagrid.prototype.showCheckboxcol = function (flag) {
        var that = this, options = that.options, model = that.columnModel, numberModel = model[0], checkModel, modelOrder = -1, checkModel_index = $.inArray(that.checkboxColumn, model)

        if (checkModel_index != -1)
            checkModel = that.columnModel[checkModel_index]

        if (checkModel) {
            if (typeof flag === 'string' && (flag === 'lock' || flag === 'unlock')) {
                that.colLock(checkModel.th, flag === 'lock' ? true : false)
            } else {
                that.showhideColumn(checkModel.th, flag)
            }
        } else if (flag) {
            modelOrder = $.inArray(that.linenumberColumn, model)
            if (modelOrder == -1)
                modelOrder = $.inArray(that.childColumn, model)

            modelOrder++
            checkModel = that.checkboxColumn
            checkModel.index = modelOrder
            model.splice(modelOrder, 0, checkModel)

            if (that.inputs && that.inputs.length)
                that.inputs = that.inputs.splice(modelOrder, 0, '')

            var $trsH = that.$thead.find('> tr'), col = '<col style="width:30px;">', $th, $td, $filterTr = $trsH.filter('.datagrid-filter'), rowspan = $trsH.length - $filterTr.length

            $th = $('<th align="center"' + (rowspan > 1 ? ' rowspan="' + rowspan + ' "' : '') + 'class="' + that.classnames.td_checkbox + (rowspan == 1 ? ' single-row' : '') + '"><div><div class="datagrid-space"></div><div class="datagrid-label"><input type="checkbox" data-toggle="icheck"></div></th>')

            that.$colgroupH.find('> col:eq(' + modelOrder + ')').before(col)
            that.$colgroupB.find('> col:eq(' + modelOrder + ')').before(col)
            that.$colgroupF && that.$colgroupF.find('> col:eq(' + modelOrder + ')').before(col)
            $filterTr.length && $filterTr.find('> th:eq(' + modelOrder + ')').before('<th></th>')
            $trsH.first().find('> th:eq(' + modelOrder + ')').before($th)
            that.$tableF && that.$tableF.find('> thead > tr > th:eq(' + modelOrder + ')').before('<th></th>')
            $th.initui()

            checkModel.th = $th
            checkModel.width = 30

            that.$tbody.find('> tr').each(function (i) {
                var $tr = $(this), colspan = that.columnModel.length

                if ($tr.hasClass(that.classnames.tr_child) || $tr.hasClass('datagrid-nodata'))
                    $tr.find('> td').attr('colspan', colspan)
                else {
                    $td = $('<td align="center" class="' + that.classnames.td_checkbox + '"><input type="checkbox" data-toggle="icheck" name="datagrid.checkbox"></td>')
                    $tr.find('> td:eq(' + modelOrder + ')').before($td)
                    $td.initui()
                }
            })

            $.each(model, function (i, n) {
                n.index = i
                if (n.th) n.th.data('index', i)
            })

            $th.find('> div').height($th.outerHeight())

            if (flag === 'lock') {
                that.colLock($th, true)
            }
            if (that.$showhide) {
                that.$showhide.remove()
                that.colShowhide(options.columnShowhide)
            }
        }
    }

    //api - showhide checkbox column
    Datagrid.prototype.showEditCol = function (flag) {
        var that = this, options = that.options, model = that.columnModel, editModel = model[model.length - 1], data = that.data

        if (editModel === that.editBtnsColumn) {
            that.showhideColumn(editModel.th, flag ? true : false)
        } else if (flag) {
            var $trsH = that.$thead.find('> tr'), col = '<col style="width:110px;">', $th, $td, $filterTr = $trsH.filter('.datagrid-filter'), rowspan = $trsH.length - $filterTr.length

            editModel = that.editBtnsColumn
            model.push(editModel)

            that.$colgroupH.append(col)
            that.$colgroupB.append(col)
            that.$colgroupF && that.$colgroupF.append(col)
            $th = $('<th align="center" rowspan="' + rowspan + '">' + that.editBtnsColumn.label + '</th>')
            $trsH.first().append($th)
            $filterTr.length && $filterTr.append('<th></th>')
            $th.initui().data('index', model.length - 1)
            editModel.th = $th
            editModel.width = 110
            editModel.index = model.length - 1

            that.$tbody.find('> tr').each(function (i) {
                var $tr = $(this), colspan = that.columnModel.length

                if ($tr.hasClass(that.classnames.tr_child) || $tr.hasClass('datagrid-nodata'))
                    $tr.find('> td').attr('colspan', colspan)
                else {
                    $td = $('<td align="center" class="' + that.classnames.s_edit + '">' + BJUI.doRegional(FRAG.gridEditBtn, that.regional) + '</td>')
                    $tr.append($td)
                    $td.initui()
                }
            })

            that.edit(that.$tbody.find('> tr'))

            that.$tableF && that.$tableF.find('> thead > tr').append('<th></th>')

            if (that.$showhide) {
                that.$showhide.remove()
                that.colShowhide(options.columnShowhide)
            }
        }
    }

    //resize
    Datagrid.prototype.colResize = function () {
        var that = this,
            tools = that.tools,
            columnModel = that.columnModel,
            $thead = that.$thead,
            $resizeMark = that.$grid.find('> .resizeProxy')

        if (!$resizeMark.length) {
            $resizeMark = $('<div class="resizeProxy" style="left:0; display:none;"></div>')
            $resizeMark.appendTo(that.$grid)
        }

        $thead.find('> tr > th').each(function (i) {
            var $th = $(this), $resize = $th.find('> div > .' + that.classnames.th_cell + '> .' + that.classnames.th_resizemark)

            $resize.on('mousedown.bjui.datagrid.resize', function (e) {
                var ofLeft = that.$boxH.scrollLeft(), marginLeft = parseInt(that.$boxH.css('marginLeft') || 0, 10), left, index = $th.data('index'), model = columnModel[index], width = model.th.width()
                    , $trs = that.$tbody.find('> tr'), $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr'), lockH = that.$lockTableB && that.$lockTableB.height(), H = that.$tableB.height(), lockH_new, H_new

                if (isNaN(marginLeft)) marginLeft = 0
                left = tools.getRight($th) - ofLeft + marginLeft

                that.isResize = true

                if (model.locked) {
                    left = tools.getRight4Lock(model.lockIndex)
                    if (model.lockWidth) width = model.lockWidth
                }

                $resizeMark
                    .show()
                    .css({
                        left: left,
                        bottom: (that.$boxP ? that.$boxP.outerHeight() : 0),
                        cursor: 'col-resize'
                    })
                    .basedrag({
                        scop: true, cellMinW: 20, relObj: $resizeMark,
                        move: 'horizontal',
                        event: e,
                        stop: function () {
                            var new_left = $resizeMark.position().left,
                                move = new_left - left,
                                newWidth = move + width,
                                tableW = that.$tableH.width() + move,
                                lockW = that.$lockTableH && that.$lockTableH.width() + move

                            if (newWidth < 5) newWidth = 20
                            if (model.minWidth && newWidth < model.minWidth) newWidth = model.minWidth
                            if (newWidth != width + move) {
                                tableW = tableW - move + (newWidth - width)
                                lockW = lockW - move + (newWidth - width)
                            }

                            model.width = newWidth

                            if (model.locked) {
                                if (lockW < (that.$grid.width() * 0.75)) {
                                    model.lockWidth = newWidth
                                    that.$lockColgroupH.find('> col:eq(' + index + ')').width(newWidth)
                                    that.$lockColgroupB.find('> col:eq(' + index + ')').width(newWidth)
                                    that.$lockColgroupF && that.$lockColgroupF.find('> col:eq(' + index + ')').width(newWidth)

                                    that.$lockTableH.width(lockW)
                                    that.$lockTableB.width(lockW)
                                    that.$lockTableF && that.$lockTableF.width(lockW)

                                    var margin = that.$lockBox.width()

                                    that.$boxH.css('margin-left', margin - 1)
                                    that.$boxB.css('margin-left', margin - 1)
                                    that.$boxH.find('> div').width(that.$boxH.width())
                                    that.$boxB.find('> div').width(that.$boxH.width())
                                    that.$boxF && that.$boxF.css('margin-left', margin - 1)

                                    that.$colgroupH.find('> col:eq(' + index + ')').width(newWidth)
                                    that.$colgroupB.find('> col:eq(' + index + ')').width(newWidth)
                                    that.$colgroupF && that.$colgroupF.find('> col:eq(' + index + ')').width(newWidth)
                                }
                            } else {
                                setTimeout(function () {
                                    that.$colgroupH.find('> col:eq(' + index + ')').width(newWidth)
                                    that.$colgroupB.find('> col:eq(' + index + ')').width(newWidth)
                                    that.$colgroupF && that.$colgroupF.find('> col:eq(' + index + ')').width(newWidth)
                                }, 1) //setTimeout for chrome
                            }

                            /* fixed height */
                            setTimeout(function () {
                                $trs.css('height', '')
                                H_new = that.$tableB.height()

                                if (that.$lockTbody) {
                                    $lockTrs.css('height', '')
                                    lockH_new = that.$lockTableB.height()
                                    if (lockH_new != lockH || H_new != H) {
                                        if (lockH_new > H_new) {
                                            $lockTrs.each(function (tr_index) {
                                                var $lockTr = $(this), $lockTd = $lockTr.find('> td:eq(' + model.lockIndex + ')'), newH = $lockTd.outerHeight()

                                                if (newH > 30) {
                                                    $lockTr.height(newH)
                                                    $trs.eq(tr_index).height(newH)
                                                }
                                            })
                                        } else {
                                            $trs.each(function (tr_index) {
                                                var $tr = $(this), $td = $tr.find('> td:eq(' + index + ')'), newH = $td.outerHeight()

                                                if (newH > 30) {
                                                    $tr.height(newH)
                                                    $lockTrs.eq(tr_index).height(newH)
                                                }
                                            })
                                        }
                                    }

                                    that.$lockB.height(that.$boxB[0].clientHeight)
                                }

                                if (model.calc) that.$tfoot && that.$tfoot.trigger('resizeH.bjui.datagrid.tfoot')

                                that.isResize = false
                            }, 10)

                            $resizeMark.hide()
                            that.resizeFlag = true
                        }
                    })
            })
        })
    }

    //column - add menu button
    Datagrid.prototype.colMenu = function () {
        var that = this, options = that.options, tools = that.tools, regional = that.regional, $ths = that.$trsH.find('> th.' + that.classnames.th_menu), $menu = that.$grid.find('> .' + that.classnames.s_menu)

        if (!$menu.legnth) {
            $menu = $(BJUI.doRegional(FRAG.gridMenu, regional))
            $menu.hide().appendTo(that.$grid)
            that.$menu = $menu
        }
        that.colShowhide(options.columnShowhide)

        $ths.each(function () {
            var $th = $(this),
                index = $th.data('index'),
                model = that.columnModel[index],
                $cell = $th.find('> div > .' + that.classnames.th_cell),
                $btnBox = $cell.find('> .' + that.classnames.btn_menu),
                $btn

            if (!$btnBox.length) {
                $btn = $('<button type="button" class="btn btn-default"><i class="fa fa-bars"></button>'),
                    $btnBox = $('<div class="' + that.classnames.btn_menu + '"></div>').append($btn)

                $btnBox.appendTo($cell)

                $btn.click(function () {
                    var $tr = $th.closest('tr'), rowspan = parseInt(($th.attr('rowspan') || 1), 10), left = $(this).offset().left - that.$grid.offset().left - 1, top = (that.$trsH.length - rowspan) * 25 + (13 * rowspan) + 11, $showhide = $menu.find('> ul > li.datagrid-li-showhide'), menu_width, submenu_width
                    var $otherBtn = $menu.data('bjui.datagrid.menu.btn')

                    if ($otherBtn && $otherBtn.length) $otherBtn.removeClass('active')
                    $(this).addClass('active')
                    if ($showhide.length && that.$showhide) {
                        that.$showhide.appendTo($showhide)
                        submenu_width = that.$showhide.data('width')
                    }
                    $menu
                        .css({ position: 'absolute', top: top, left: left })
                        .show()
                        .data('bjui.datagrid.menu.btn', $btn)
                        .siblings('.' + that.classnames.s_menu).hide()

                    menu_width = $menu.outerWidth()

                    var position = function (left) {
                        if (that.$boxH.width() - left < menu_width) {
                            $menu.css({ left: left - menu_width + 18 }).addClass('position-right')
                        } else {
                            $menu.css({ left: left }).removeClass('position-right')
                        }
                    }
                    var fixedLeft = function ($btn) {
                        that.$boxB.scroll(function () {
                            var left = $btn.offset().left - that.$grid.offset().left - 1

                            position(left)
                        })
                    }

                    position(left)
                    fixedLeft($btn)

                    if (options.columnFilter && (!model.type || model.type != 'select')) that.colFilter($th)
                    else $menu.find('> ul > li.' + that.classnames.li_filter).hide()

                    tools.locking($th)

                    // quick sort
                    var $asc = $menu.find('> ul > li.' + that.classnames.li_asc),
                        $desc = $asc.next()

                    $asc.off('click.bjui.datagrid.sort').on('click.bjui.datagrid.sort', function () {
                        if (model.sortDesc)
                            model.sortDesc = false
                        else {
                            model.sortAsc = false
                        }
                        tools.quickSort(model)
                    })

                    $desc.off('click.bjui.datagrid.sort').on('click.bjui.datagrid.sort', function () {
                        if (model.sortAsc) {
                            model.sortDesc = false
                        } else {
                            model.sortAsc = true
                        }
                        tools.quickSort(model)
                    })

                    $menu.on('sort.bjui.datagrid.th', function (e, asc) {
                        $asc.toggleClass('sort-active', asc)
                        $desc.toggleClass('sort-active', !asc)
                    })
                })
            }
        })

        /* hide filterbox */
        that.$grid.on('click.bjui.datagrid.filter', function (e) {
            var $target = $(e.target), $menu = that.$grid.find('.' + that.classnames.s_menu + ':visible')

            if ($menu.length && !$target.closest('.' + that.classnames.btn_menu).length) {
                if (!$target.closest('.' + that.classnames.s_menu).length) {
                    $menu.hide().data('bjui.datagrid.menu.btn').removeClass('active')
                }
            }
        })
    }

    // show or hide columns on btn menu
    Datagrid.prototype.colShowhide = function (showFlag) {
        var that = this, options = that.options, tools = that.tools, $menu = that.$menu, $ul = $menu.find('> ul'), $showhideli = $ul.find('> li.' + that.classnames.li_showhide)

        if (showFlag) {
            if (!that.$showhide) {
                tools.createShowhide()
                tools.showSubMenu($showhideli, $menu, that.$showhide)
            }
        } else {
            $showhideli.hide()
        }
    }

    // api - show or hide column
    Datagrid.prototype.showhideColumn = function (column, showFlag) {
        var that = this, tools = that.tools, index, model

        if ($.type(column) === 'number') {
            index = parseInt(column, 10)
            if (index < 0) return
        } else {
            index = column.data('index')
        }

        model = that.columnModel[index]

        if (model) {
            if (model.locked) {
                if (showFlag) return
                else that.colLock(model.th, showFlag)
            }
            tools.showhide(model, showFlag)
        }
    }

    // filter
    Datagrid.prototype.colFilter = function ($th, filterFlag) {
        var that = this, options = that.options, tools = that.tools, regional = that.regional, $filter = $th.data('bjui.datagrid.filter'), $menu = that.$menu, $filterli = $menu.find('> ul > li.' + that.classnames.li_filter)
        var model = that.columnModel[$th.data('index')]

        if (!that.inputs || !that.inputs.length) tools.initEditInputs()
        if (model.quickfilter) {
            $filterli.show().find('.' + that.classnames.s_filter).addClass('hide')

            if (!$filter || !$filter.length) {
                $filter = $(BJUI.doRegional(FRAG.gridFilter.replaceAll('#label#', $th.text()), regional)).hide().appendTo(that.$grid)

                var index = $th.data('index'), model = that.columnModel[index], type = model.type || 'string', operator = model.operator || [],
                    $filterA = $filter.find('span.filter-a'),
                    $filterB = $filter.find('span.filter-b'),
                    $select = $('<select data-toggle="selectpicker" data-container="true"></select>'),
                    $input = $(that.inputs[index]),
                    $valA, $valB

                $input.removeAttr('data-rule').attr('size', 10).addClass('filter-input')

                if (type === 'string' || type === 'findgrid' || type === 'tags') {
                    if (!operator.length) operator = ['=', '!=', 'like']
                    if (type === 'findgrid') {
                        $input.data('context', $filter)
                    }
                } else if (type === 'number' || type === 'int' || type === 'spinner') {
                    if (type === 'spinner') $input.removeAttr('data-toggle')
                    if (!operator.length) operator = ['=', '!=', '>', '<', '>=', '<=']
                } else if (type === 'date') {
                    //if (!operator.length) operator = ['=', '!=']
                    if (!operator.length) operator = ['>=', '<=', '=', '!=', '>', '<']
                } else if (type === 'boolean') {
                    if (!operator.length) operator = ['=', '!=']
                    $input = $(BJUI.doRegional('<select name="' + model.name + '" data-toggle="selectpicker"><option value="">#all#</option><option value="true">#true#</option><option value="false">#false#</option></select>', regional))
                } else if (type === 'select') {
                    if (!operator.length) operator = ['=', '!=']
                    $input.attr('data-width', '80')
                    if ($input.find('> option:first-child').val()) {
                        $input = $('<select name="' + model.name + '" data-toggle="selectpicker" data-width="80"></select>')
                            .append(BJUI.doRegional('<option value="">#all#</option>', regional))
                            .append($input.html())
                    }
                }

                for (var i = 0, l = operator.length; i < l; i++) {
                    $select.append('<option value="' + (operator[i]) + '">' + (operator[i]) + '</option>')
                }

                $valA = $input
                $valB = $valA.clone()

                $filterA.append($select).append($input)
                $filterB.append($select.clone()).append($valB)

                $th.data('bjui.datagrid.filter', $filter)

                $filter.appendTo($filterli)
                $filter.data('width', $filter.outerWidth()).hide().initui()

                /* events */
                var $ok = $filter.find('button.ok'),
                    $clear = $filter.find('button.clear'),
                    $selects = $filter.find('select'),
                    $selA = $selects.first(),
                    $selB = $selects.last(),
                    $andOr = $selects.eq(1)

                $ok.click(function () {
                    var operatorA = $selA.val(), valA = $valA.val().trim(), operatorB = $selB.val(), valB = $valB.val().trim(), andor = $andOr.val()
                    var filterDatas = {}

                    if (valA.length) {
                        $.extend(filterDatas, { operatorA: operatorA, valA: valA })
                    }
                    if (valB.length) {
                        if (valA.length) $.extend(filterDatas, { andor: andor })
                        $.extend(filterDatas, { operatorB: operatorB, valB: valB })
                    }
                    if (!$.isEmptyObject(filterDatas)) {
                        tools.quickFilter(that.columnModel[$th.data('index')], filterDatas)
                        that.$grid.trigger('click')
                        $filterli.trigger('hidesubmenu.bjui.datagrid.th', [$menu, $filter])
                    } else {
                        $clear.trigger('click')
                    }
                })

                $clear.click(function () {
                    var model = that.columnModel[$th.data('index')]

                    $selects.find('> option:first').prop('selected', true)
                    $selects.selectpicker('refresh')
                    $valA.val('')
                    $valB.val('')
                    if (model.isFiltered) {
                        tools.quickFilter(model, null)
                        that.$grid.trigger('click')
                        $filterli.trigger('hidesubmenu.bjui.datagrid.th', [$menu, $filter])
                    }
                })
            }

            tools.showSubMenu($filterli, $menu, $filter.removeClass('hide'))

            $menu.find('> ul > li:not(".' + that.classnames.li_filter + '")').on('mouseover', function () {
                if ($filterli.hasClass('active'))
                    $filterli.trigger('hidesubmenu.bjui.datagrid.th', [$menu, $filter])
            })
        } else {
            $filterli.hide()
        }
    }

    // paging
    Datagrid.prototype.initPaging = function () {
        var that = this, tools = that.tools, options = that.options, paging = that.paging, pr = BJUI.regional.pagination, btnpaging = FRAG.gridPaging, pageNums = [], pageCount = paging.pageCount, interval, selectPages = [], pagingHtml = BJUI.StrBuilder()

        interval = tools.getPageInterval(pageCount, paging.pageCurrent, paging.showPagenum)

        for (var i = interval.start; i < interval.end; i++) {
            pageNums.push(FRAG.gridPageNum.replace('#num#', i).replace('#active#', (paging.pageCurrent == i ? ' active' : '')))
        }

        btnpaging = BJUI.doRegional(btnpaging.replaceAll('#pageCurrent#', paging.pageCurrent).replaceAll('#count#', paging.pageCurrent + '/' + parseInt((pageCount || 0), 10)).replaceAll('#totalCount#', "共" + paging.total + "条"), pr)

        pagingHtml
            .add('<div class="paging-content" style="width:' + that.$boxB.width() + 'px;">')
            .add('<span></span><div class="paging-pagesize"><button type="button" class="btn-default btn-refresh" title="' + pr.refresh + '" data-icon="refresh"></button>')
            .add('<select data-toggle="selectpicker"></select>')
            .add('</div>')
            .add('<div class="paging-box">')
            .add(btnpaging.replace('#pageNumFrag#', pageNums.join('')))
            .add('</div>')
            .add('</div>')

        that.$boxP.html(pagingHtml.toString())
        that.$boxP.initui()

        //events
        var $select = that.$boxP.find('div.paging-pagesize > select'),
            $pagenum = that.$boxP.find('ul.pagination'),
            $pagetotal = $pagenum.find('> li.page-total'),
            $pagetotalCount = $pagenum.find('> li.page-totalCount'),
            $jumpto = $pagetotal.next(),
            $first = $jumpto.next(),
            $prev = $first.next(),
            $next = $prev.nextAll('.page-next'),
            $last = $next.next()

        var disablePrev = function () {
            $first.addClass('disabled')
            $prev.addClass('disabled')
        }
        var enablePrev = function () {
            $first.removeClass('disabled')
            $prev.removeClass('disabled')
        }
        var disableNext = function () {
            $next.addClass('disabled')
            $last.addClass('disabled')
        }
        var enableNext = function () {
            $next.removeClass('disabled')
            $last.removeClass('disabled')
        }
        var pageFirst = function () {
            disablePrev()
            enableNext()
        }
        var pageLast = function () {
            enablePrev()
            disableNext()
        }
        var setPageSize = function (pageSize) {
            $select.empty()

            if (!pageSize) pageSize = that.paging.pageSize

            selectPages = paging.selectPageSize.split(',')
            selectPages.push(String(pageSize))

            $.unique(selectPages).sort(function (a, b) { return a - b })

            var opts = []

            $.each(selectPages, function (i, n) {
                opts.push('<option value="' + n + '"' + (n == paging.pageSize && 'selected') + '>' + pr.eachpage + n + '' + pr.unit + '</option>')
            })

            $select.html(opts.join('')).selectpicker('refresh')
        }

        if (paging.pageCurrent == 1) pageFirst()
        if (paging.pageCurrent == paging.pageCount) {
            pageLast()
            if (paging.pageCurrent == 1) disablePrev()
        }
        if (!paging.total) disableNext()
        setPageSize()

        that.$boxP.on('click.datagrid.pagenum', 'li.page-num', function (e) {
            var $num = $(this)

            if (!$num.hasClass('active')) {
                that.jumpPage($num.text())
            }

            e.preventDefault()
        }).on('click.datagrid.refresh', 'button.btn-refresh', function () {
            that.refresh()
        }).on('bjui.datagrid.paging.jump', function (e) {
            var pageCurrent = that.paging.pageCurrent, interval = tools.getPageInterval(that.paging.pageCount, pageCurrent, paging.showPagenum), pageNums = []

            for (var i = interval.start; i < interval.end; i++) {
                pageNums.push(FRAG.gridPageNum.replace('#num#', i).replace('#active#', (pageCurrent == i ? ' active' : '')))
            }

            $pagenum.find('> li.page-num').remove()
            $prev.after(pageNums.join(''))

            if (pageCurrent == 1) {
                pageFirst()
                if (pageCurrent == that.paging.pageCount) disableNext()
                if (!that.paging.total) disableNext()
            } else if (pageCurrent == that.paging.pageCount) {
                pageLast()
            } else {
                enablePrev()
                enableNext()
            }

            $jumpto.find('input').val(pageCurrent)
            $pagetotal.find('span:first').html(that.paging.pageCurrent + '/' + that.paging.pageCount)
            $pagetotalCount.find('span:first').attr('title', pr.ttl + that.paging.total + pr.unit).html(pr.ttl + that.paging.total + pr.unit)
        }).on('bjui.datagrid.paging.pageSize', function (e, pageSize) {
            setPageSize(pageSize)
        }).on('change', 'div.paging-pagesize > select', function () {
            var pageSize = $(this).val()

            that.jumpPage(null, pageSize)
        })

        $jumpto.find('input').on('keyup', function (e) {
            if (e.which === BJUI.keyCode.ENTER) {
                var page = $(this).val()

                if (page) that.jumpPage(page)
            }
        })

        $first.on('click', function () {
            if (that.paging.pageCurrent > 1)
                that.jumpPage(1)
        })

        $prev.on('click', function () {
            if (that.paging.pageCurrent > 1)
                that.jumpPage(that.paging.pageCurrent - 1)
        })

        $next.on('click', function () {
            if (that.paging.pageCurrent < that.paging.pageCount)
                that.jumpPage(that.paging.pageCurrent + 1)
        })

        $last.on('click', function () {
            if (that.paging.pageCurrent < that.paging.pageCount)
                that.jumpPage(that.paging.pageCount)
        })
    }

    Datagrid.prototype.jumpPage = function (pageCurrent, pageSize) {
        var that = this, paging = that.paging, pageCount = paging.pageCount

        if (pageCurrent && isNaN(pageCurrent)) return
        if (pageSize && isNaN(pageSize)) return
        if (pageCurrent) {
            pageCurrent = parseInt(pageCurrent, 10)

            if (pageCurrent < 1) pageCurrent = 1
            if (pageCurrent > pageCount) pageCurrent = pageCount
            if (pageCurrent == paging.pageCurrent) return
        }
        if (pageSize) {
            pageSize = parseInt(pageSize, 10)

            if (that.options.local != 'remote') {
                if (paging.pageSize > paging.total) return
            }
        }

        that.tools.jumpPage(pageCurrent, pageSize)
    }

    // api - add
    Datagrid.prototype.add = function (addLocation, addData, noEdit) {
        if (!this.tools.beforeEdit() && !noEdit) return

        if (!this.options.editUrl && !noEdit) {
            BJUI.debug('BJUI.Datagrid: Edit url is not set!')
            return
        }

        if (!this.options.editMode && !noEdit) return
        if (!this.options.inlineEditMult) {
            this.doCancelEditRow(this.$tbody.find('> tr.' + this.classnames.tr_edit))
        } else if (!noEdit && this.options.editMode != 'inline' && this.$tbody.find('> tr.' + this.classnames.tr_add).length) {
            return
        }

        if (!addData) {
            addData = {}
        } else {
            if (typeof addData === 'string') {
                addData = addData.toObj()
            }
            if (typeof addData !== 'object') {
                addData = {}
            }
        }

        var that = this, options = that.options, keys = options.keys, tools = that.tools, paging = that.paging, trs, obj = {}, data = [], addObj, startNumber = (paging.pageCurrent - 1) * paging.pageSize, linenumber

        var addTr = function () {
            var $tr = $('<tr></tr>').addClass(that.classnames.tr_add), $lockTr = $tr.clone()

            if (that.isTemplate) {
                var tdTemplate = options.tdTemplate, tempcolspan = that.columnModel.length

                if (typeof tdTemplate === 'function')
                    tdTemplate = tdTemplate.apply(that, [{}])

                if (that.options.hasChild && that.options.childOptions) {
                    $tr.append('<td data-title="..." align="center" class="datagrid-child-td"><div>' + BJUI.doRegional(FRAG.gridExpandBtn, that.regional) + '</div></td>')
                    tempcolspan--
                }
                if (that.options.showLinenumber) {
                    $tr.append('<td data-title="No." align="center" class="datagrid-linenumber-td">0</td>')
                    tempcolspan--
                }
                if (that.options.showCheckboxcol) {
                    $tr.append('<td data-title="Checkbox" align="center" class="datagrid-checkbox-td"><div><input type="checkbox" data-toggle="icheck" name="datagrid.checkbox" value="true"></div></td>')
                    tempcolspan--
                }

                $tr.append('<td class="datagrid-template-td" colspan="' + tempcolspan + '">' + tdTemplate + '</td>')
            } else {
                $.each(that.columnModel, function (i, n) {
                    var label = '', $td = $('<td></td>')

                    if (n[keys.gridChild])
                        $td.addClass(that.classnames.td_child).html('<div>' + BJUI.doRegional(FRAG.gridExpandBtn, that.regional) + '</div>')
                    else if (n[keys.gridNumber])
                        $td.addClass(that.classnames.td_linenumber).text(0)
                    else if (n[keys.gridCheckbox])
                        $td.addClass(that.classnames.td_checkbox).html('<div><input type="checkbox" data-toggle="icheck" name="datagrid.checkbox" value="true"></div>')
                    else if (n[keys.gridEdit])
                        $td.addClass(that.classnames.s_edit).data('isAdd', true).html(BJUI.doRegional(FRAG.gridEditBtn, that.regional))
                    else $td.text('')

                    if (n.hidden) $td.css('display', 'none')
                    if (n.align) $td.attr('align', n.align)

                    if (n.name) obj[n.name] = ''

                    if (n.locked) {
                        if (n[keys.gridCheckbox]) {
                            $td.clone().hide().appendTo($tr)
                            $td.show().appendTo($lockTr)
                        } else {
                            $td.clone().show().appendTo($lockTr)
                            $td.hide().appendTo($tr)
                        }
                    } else {
                        $td.appendTo($tr)
                    }
                })
            }

            obj = $.extend({}, that.attach, obj)
            if (!that.emptyData) that.emptyData = obj

            return { tr: $tr, lockTr: $lockTr.find('> td').length ? $lockTr : null }
        }

        if (!addLocation) addLocation = options.addLocation || 'first'
        if (!that.$lastSelect) {
            if (addLocation === 'prev') addLocation = 'first'
            else if (addLocation === 'next') addLocation = 'last'
        }
        if ($.inArray(addLocation, ['first', 'last', 'prev', 'next']) === -1)
            addLocation = 'first'

        if (options.isTree && that.$lastSelect) {
            if (!(addLocation === 'first' || addLocation === 'last'))
                addLocation = 'first'

            var parentData = that.data[that.tools.getNoChildDataIndex(that.$lastSelect.index())]

            if (parentData) {
                addData[options.treeOptions.keys.level] = parentData[options.treeOptions.keys.level] + 1
                addData[options.treeOptions.keys.parentKey] = parentData[options.treeOptions.keys.key]
                addData[options.keys.treePTr] = that.$lastSelect
                addData[options.keys.treePData] = parentData
            } else {
                addData['level'] = 0
            }
        }

        if (addLocation === 'first') {
            linenumber = 0
            trs = addTr()

            if (options.isTree && that.$lastSelect) {
                linenumber = that.tools.getNoChildDataIndex(that.$lastSelect.index()) + 1

                if (that.$lastSelect.next().hasClass(that.classnames.tr_child)) {
                    trs.tr.insertAfter(that.$lastSelect.next())
                    if (trs.lockTr) trs.lockTr.insertAfter(that.$lockTbody.find('> tr:eq(' + that.$lastSelect.next().index() + ')'))
                } else {
                    trs.tr.insertAfter(that.$lastSelect)
                    if (trs.lockTr) trs.lockTr.insertAfter(that.$lockTbody.find('> tr:eq(' + that.$lastSelect.index() + ')'))
                }
            } else {
                trs.tr.prependTo(that.$tbody)
                if (trs.lockTr) trs.lockTr.prependTo(that.$lockTbody)
            }
        } else if (addLocation === 'last') {
            linenumber = that.$tbody.find('> tr').length
            trs = addTr()

            if (options.isTree && that.$lastSelect) {
                var child = that.$lastSelect.data('child') || 0, level = that.$lastSelect.data('level'), $lastTr, hasChild = that.$lastSelect.next().hasClass(that.classnames.tr_child)

                $lastTr = that.getChildrens(that.$lastSelect, that.$lastSelect).last()

                if (hasChild) {
                    trs.tr.insertAfter($lastTr.next())
                    if (trs.lockTr) trs.lockTr.insertAfter(that.$lockTbody.find('> tr:eq(' + $lastTr.next().index() + ')'))
                } else {
                    trs.tr.insertAfter($lastTr)
                    if (trs.lockTr) trs.lockTr.insertAfter(that.$lockTbody.find('> tr:eq(' + $lastTr.index() + ')'))
                }

                linenumber = that.tools.getNoChildDataIndex($lastTr.index()) + 1

                addData[options.treeOptions.keys.order] = child
            } else {
                trs.tr.appendTo(that.$tbody)
                if (trs.lockTr) trs.lockTr.appendTo(that.$lockTbody)
            }
        } else if (addLocation === 'prev') {
            linenumber = that.tools.getNoChildDataIndex(that.$lastSelect.index())
            trs = addTr()

            trs.tr.insertBefore(that.$lastSelect)
            if (trs.lockTr) trs.lockTr.insertBefore(that.$lockTbody.find('> tr:eq(' + that.$lastSelect.index() + ')'))
        } else if (addLocation === 'next') {
            linenumber = that.tools.getNoChildDataIndex(that.$lastSelect.index()) + 1
            trs = addTr()

            if (that.$lastSelect.next().hasClass(that.classnames.tr_child)) {
                trs.tr.insertAfter(that.$lastSelect.next())
                if (trs.lockTr) trs.lockTr.insertAfter(that.$lockTbody.find('> tr:eq(' + that.$lastSelect.next().index() + ')'))
            } else {
                trs.tr.insertAfter(that.$lastSelect)
                if (trs.lockTr) trs.lockTr.insertAfter(that.$lockTbody.find('> tr:eq(' + that.$lastSelect.index() + ')'))
            }
        }

        addData = $.extend({}, that.emptyData, addData, { addFlag: true })

        if (!that.data) that.data = []
        if (!that.allData) that.allData = []
        if (that.allData.length) {
            that.allData.splice(linenumber + startNumber, 0, addData)
        } else {
            that.allData.push(addData)
        }

        if (that.data.length) {
            that.data.splice(linenumber, 0, addData)
        } else {
            that.data.push(addData)
        }

        that.tools.updateGridIndex()

        trs.tr.initui()
        trs.lockTr && trs.lockTr.initui()

        if (addData[options.keys.treePTr])
            trs.tr.data(options.keys.treePTr, addData[options.keys.treePTr])
        if (options.showNoDataTip)
            that.$tbody.find('> tr.datagrid-nodata').remove()
        if (options.height === 'auto')
            that.fixedHeight()

        setTimeout(function () {
            that.initEvents(trs.tr)
        }, 20)

        if (trs.lockTr) that.initLockEvents(trs.lockTr)
        that.edit(trs.tr)

        if (options.hasChild && options.childOptions)
            trs.tr.after('<tr class="' + that.classnames.tr_child + '"></tr>')

        if (noEdit) {
            that.inlineEditComplete(trs.tr, addData)
            if (that.data.length == 1)
                that.needfixedWidth = true
            that.tools.afterSave(trs.tr, addData)
        } else {
            if (options.editMode != 'dialog') {
                that.doEditRow(trs.tr, 'inline', true)
            } else {
                that.dialogEdit(trs.tr, true)
            }
        }
    }

    // edit
    Datagrid.prototype.edit = function ($trs) {
        var that = this, options = that.options, tools = that.tools, type = options.editMode, columnModel = that.columnModel, $editTd

        that.editInit = false

        if (!type) return
        if (typeof type === 'object') {
            var editOptions = {}, types = ['dialog', 'navtab', 'div'], editFlag = false

            that.editOptions = null

            for (var key in type) {
                if ($.inArray(key, types) != -1) {
                    editOptions.type = key
                    editOptions.options = type[key]

                    editFlag = true

                    break
                }
            }

            if (!editFlag) {
                BJUI.debug('Dialog Plugn: The options \'editModel\' set error!')
                return
            }

            if (!$.isEmptyObject(editOptions))
                that.editOptions = editOptions

        } else {
            if (that.options.editUrl)
                that.options.editUrl = that.options.editUrl.replace(/{\/?[^}]*}/g, '')

            if (type != 'dialog')
                type = 'inline'
        }

        if (!$trs) $trs = that.$tbody.find('> tr')

        $editTd = $trs.find('> td.' + that.classnames.s_edit)

        that.editInit = true

        /* events */
        $editTd.each(function () {
            var $td = $(this), $btns = $td.find('button.bjui-datagrid-btn'), $edit = $btns.first(), $update = $edit.next(), $save = $update.next(), $cancel = $save.next(), $delete = $cancel.next()

            $edit.on('click', function (e, data) {
                var $btn = $(this), $tr = $btn.closest('tr'), data_index = $tr.index(), isAdd = $td.data('isAdd')

                data_index = that.tools.getNoChildDataIndex(data_index)

                if (!data) {
                    if (that.isDom) data = $tr.data('initData') || tools.setDomData($tr)
                    else data = that.data[data_index]
                }
                if (!tools.beforeEdit($tr, data)) {
                    return false
                }
                if (that.editOptions) {
                    that.externalEdit($tr, null, data)
                    return false
                }
                if (type != 'dialog') {
                    that.inlineEdit($tr, isAdd)

                    $btns.hide()
                    $update.show()
                    $cancel.show()

                    if (isAdd) {
                        $update.hide()
                        $save.show()
                    }
                } else {
                    that.dialogEdit($tr, isAdd)
                }

                e.stopPropagation()
            })

            $save.on('click', function (e) {
                var $btn = $(this), $tr = $btn.closest('tr')

                that.updateEdit($tr, $btn)

                e.stopPropagation()
            }).on('bjui.datagrid.update.tr', function () {
                $btns.hide()
                $edit.show()
                $delete.show()
            })

            $update.on('click', function (e) {
                var $btn = $(this), $tr = $btn.closest('tr')

                that.updateEdit($tr, $btn)

                e.stopPropagation()
            }).on('bjui.datagrid.update.tr', function () {
                $btns.hide()
                $edit.show()
                $delete.show()
            })

            $cancel.on('click', function (e) {
                var $btn = $(this), $tr = $btn.closest('tr')

                that.cancelEdit($tr)

                $btns.hide()
                $edit.show()
                $delete.show()

                e.stopPropagation()
            })

            $delete.on('click', function (e) {
                var $btn = $(this), $tr = $btn.closest('tr')

                that.delRows($tr)

                e.stopPropagation()
            })
        })
    }

    Datagrid.prototype.externalEdit = function (row, editOpts, data) {
        var that = this, options = that.options, editOptions = {}, $tr, data_index, editUrl, type, types = ['dialog', 'navtab', 'div']

        if (editOpts && typeof editOpts === 'object') {
            for (var key in editOpts) {
                if ($.inArray(key, types) != -1) {
                    editOptions.type = key
                    editOptions.options = editOpts[key]

                    break
                }
            }
        } else
            editOptions = $.extend({}, that.editOptions)

        if (row instanceof jQuery) {
            $tr = row
        } else if (!isNaN(row)) {
            $tr = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')').eq(parseInt(row, 10))
        } else {
            BJUI.debug('BJUI.Datagrid: Func \'externalEdit\', Parameter \'row\' is incorrect!')
            return
        }

        if (typeof editOptions === 'undefined') {
            BJUI.debug('BJUI.Datagrid: Func \'externalEdit\', Parameter \'editOptions\' is incorrect!')
            return
        }

        if (!$.isEmptyObject(editOptions)) {
            if (!data) {
                if (that.isDom) data = $tr.data('initData') || that.tools.setDomData($tr)
                else {
                    data_index = that.tools.getNoChildDataIndex($tr.index())
                    data = that.data[data_index]
                }
            }
            if (!editOptions.options.url || editOptions.options['datagrid.nourl']) {
                editOptions.options['datagrid.nourl'] = true
                editOptions.options.url = options.editUrl
            }

            editUrl = editOptions.options.url

            if (editUrl && !editUrl.isFinishedTm()) {
                if (!data || data.addFlag)
                    editUrl = editUrl.replace(/{\/?[^}]*}/g, '')
                else
                    editUrl = that.tools.replacePlh(editUrl, data)

                if (!editUrl.isFinishedTm()) {
                    BJUI.debug('BJUI.Datagrid: Func \'externalEdit\', the property \'url\' of options \'editOptions\' is incorrect: ' + editUrl)
                } else {
                    editUrl = editUrl
                }
            }

            if (editUrl) {
                editOptions.options.url = editUrl
                type = editOptions.type

                if (typeof data === 'object' && data) {
                    if (editOptions.options.data)
                        $.extend(editOptions.options.data, data)
                    else
                        editOptions.options.data = $.extend({}, data)
                }

                for (var key in options.keys)
                    delete editOptions.options.data[key]

                if (!editOptions.options.id)
                    editOptions.options.id = 'datagrid-external-edit' + (new Date().getTime())
                if (!editOptions.options.type)
                    editOptions.options.type = options.editType

                // onClose
                if (typeof options.extOnClose === 'undefined') {
                    options.extOnClose = (editOptions.options.onClose && editOptions.options.onClose.toFunc()) || false
                }
                // for dialog && navtab (if not save)
                editOptions.options.onClose = function () {
                    if ($tr.hasClass(that.classnames.tr_add))
                        that.cancelEdit($tr)

                    if (typeof options.extOnClose !== 'undefined') {
                        if (options.extOnClose) {
                            options.extOnClose.apply(that)
                        }
                    }
                }

                var okCallback = function (json) {
                    var complete = false, returnData, fixedWidth = false

                    if (editOptions.options.data['addFlag'] && that.data.length == 1)
                        that.needfixedWidth = true
                    if ($.type(json) === 'array') {
                        complete = true
                        returnData = json[0]
                    } else if (typeof json === 'object') {
                        complete = true
                        returnData = json
                    }

                    if (complete) {
                        $.extend(data, typeof returnData === 'object' && returnData)

                        if (that.allData && that.allData[data.gridIndex]) {
                            $.extend(that.allData[data.gridIndex], data)
                        }

                        // update allData for filter
                        if (that.oldAllData) that.oldAllData = that.allData.concat()

                        if (data.addFlag) data.addFlag = false

                        that.dialogEditComplete($tr, data)

                        if (type === 'dialog') {
                            if (editOptions.options.message)
                                BJUI.alertmsg('ok', editOptions.options.message)
                            if (typeof editOptions.options.closeCurrent === 'undefined' || editOptions.options.closeCurrent)
                                BJUI.dialog('close', editOptions.options.id)
                        }
                        else if (type === 'navtab')
                            BJUI.navtab('closeTab', editOptions.options.id)

                        that.tools.afterSave($tr, data)

                        // refresh child
                        if (data['refresh.datagrid.child']) {
                            var $child = $tr.data('bjui.datagrid.child')

                            if ($child && $child.length)
                                $child.datagrid('refresh')
                            else
                                that.showChild($tr)
                        }
                    }
                }

                editOptions.options.onLoad = function ($box) {
                    var $form = $box.find('form.datagrid-edit-form')

                    if ($form && $form.length) {
                        $form = $form.first()

                        $form
                            .removeAttr('okCallback')
                            .data('okCallback', $.proxy(okCallback, that))
                    }
                }

                if (type === 'dialog')
                    BJUI.dialog(editOptions.options)
                else if (type === 'navtab')
                    BJUI.navtab(editOptions.options)
                else if (type === 'div')
                    BJUI.ajax('doload', editOptions.options)

            }
        }
    }

    // Api - inline edit tr
    Datagrid.prototype.doEditRow = function (rows, type, isAdd) {
        if (!this.editInit) return

        var that = this, $trs, $editBtn, datas = []

        type = type || that.options.editMode

        if (typeof rows === 'object') {
            $trs = rows
        } else if (isNaN(rows)) {
            var $myTrs = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')'), $editTrs, rows = rows.split(',')

            rows = rows.unique()
            rows.sort(function (a, b) { return a.trim() - b.trim() })

            $.each(rows, function (i, n) {
                var row = parseInt(n.trim(), 10), tr

                tr = $myTrs.eq(row)

                if (tr && tr.length) {
                    if (!$editTrs) $editTrs = tr
                    else $editTrs = $editTrs.add(tr)
                }
            })

            $trs = $editTrs
        } else if (!isNaN(rows)) {
            $trs = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')').eq(rows)
        }

        if (!$trs.length) return

        $trs.each(function () {
            var $tr = $(this), data_index = $tr.index(), data

            data_index = that.tools.getNoChildDataIndex(data_index)

            if (that.isDom) data = $tr.data('initData') || tools.setDomData($tr)
            else data = that.data[data_index]

            datas.push(data)
        })

        if (!that.tools.beforeEdit($trs, datas)) {
            return
        }

        if (!that.options.editUrl) {
            BJUI.debug('BJUI.Datagrid: Edit url is not set!')
            return
        }

        if (that.editOptions) {
            that.externalEdit($trs.last(), null, datas[$trs.length - 1])
        } else {
            $trs.each(function (i) {
                var $tr = $(this)

                $editBtn = $tr.find('> td.' + that.classnames.s_edit + ' button.bjui-datagrid-btn.edit')

                if (type != 'dialog') {
                    if ($editBtn.length) $editBtn.trigger('click', [datas[i]])
                    else that.inlineEdit($tr, isAdd, datas[i])
                } else {
                    that.dialogEdit($tr, isAdd, datas[i])
                    return false
                }
            })
        }
    }

    // Api - cancel edit
    Datagrid.prototype.doCancelEditRow = function (row) {
        var that = this, $trs

        if ($.type(row) === 'number') {
            $trs = this.$tbody.find('> tr').eq(row)
        } else {
            $trs = row
        }

        $trs.each(function () {
            var $tr = $(this), $cancelBtn = $tr.find('> td.' + that.classnames.s_edit + ' > button.bjui-datagrid-btn.cancel')

            if ($cancelBtn.length) {
                $cancelBtn.trigger('click')
            } else {
                that.cancelEdit($tr)
            }
        })
    }

    // Api - save edit tr
    Datagrid.prototype.doSaveEditRow = function (row) {
        var that = this, options = that.options, $tr

        if ($.type(row) === 'number') {
            $tr = this.$tbody.find('> tr').eq(row)
        } else if (row) {
            $tr = row
        } else {
            $tr = that.$tbody.find('> tr.' + that.classnames.tr_edit)
        }

        if (!$tr.length) {
            that.$grid.alertmsg('info', BJUI.getRegional('datagrid.saveMsg'))
            return
        }
        if ($tr.length == 1) {
            if ($tr.hasClass(that.classnames.tr_edit))
                this.updateEdit($tr)
        } else {
            if (options.saveAll) {
                that.saveAll($tr)
            } else {
                $tr.each(function () {
                    that.updateEdit($(this))
                })
            }
        }
    }

    // Api - del tr
    Datagrid.prototype.delRows = function (rows) {
        var that = this, options = that.options, keys = options.keys, beforeDelete = options.beforeDelete, confirmMsg = options.delConfirm, $trs

        if (beforeDelete) {
            if (typeof beforeDelete === 'string') beforeDelete = beforeDelete.toFunc()
            if (typeof beforeDelete === 'function') {
                if (!beforeDelete.call(this)) {
                    return
                }
            }
        }

        if (typeof rows === 'object') {
            $trs = rows
        } else if (isNaN(rows)) {
            var $myTrs = that.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')'), $delTrs, rows = rows.split(',')

            rows = rows.unique()
            rows.sort(function (a, b) { return a.trim() - b.trim() })

            $.each(rows, function (i, n) {
                var tr = $myTrs.eq(parseInt(n.trim(), 10))

                if (tr && tr.length) {
                    if (!$delTrs) $delTrs = tr
                    else $delTrs = $delTrs.add(tr)
                }
            })

            $trs = $delTrs
        } else if (!isNaN(rows)) {
            $trs = this.$tbody.find('> tr:not(.' + that.classnames.tr_child + ')').eq(rows)
        }

        if (!$trs || !$trs.length) return

        var delEnd = function () {
            $trs.each(function () {
                var $tr = $(this), childUpdate = that.options.childUpdate, $parent = that.$element.data('bjui.datagrid.parent'),
                    updateParent = function ($parent) {
                        $parent.closest('table').datagrid('updateRow', $parent)
                    }
                // update child parent
                if ($parent && childUpdate) {
                    if (typeof childUpdate === 'string') {
                        if (childUpdate.indexOf('all') !== -1 || childUpdate.indexOf('del') !== -1)
                            updateParent($parent)
                    } else {
                        updateParent($parent)
                    }
                }

                $tr.trigger('delete.bjui.datagrid.tr')
            })

            that.tools.afterDelete()

            if (options.height === 'auto')
                that.fixedHeight()
        }

        var doDel = function () {
            var $addTrs = $trs.filter('.' + that.classnames.tr_add)

            if ($addTrs.length) {
                that.cancelEdit($addTrs)

                if (!$trs.not($addTrs).length)
                    return
            }

            // remote delete
            if (options.delUrl) {
                var postData = [], callback = options.delCallback

                $trs.not($addTrs).each(function () {
                    var $tr = $(this), index = $tr.index(), data, delData

                    if (that.isDom) data = $tr.data('initData') || that.tools.setDomData($tr)
                    else data = that.data[that.tools.getNoChildDataIndex(index)]

                    if (options.delPK) {
                        postData.push(data[options.delPK])
                    } else {
                        if (options.jsonPrefix) {
                            delData = {}

                            $.each(data, function (name, value) {
                                if (!that.tools.isGridData(name))
                                    delData[options.jsonPrefix + '.' + name] = value
                            })
                        } else {
                            delData = $.extend({}, data)
                            for (var key in keys)
                                delete delData[keys[key]]
                        }

                        postData.push(delData)
                    }
                })

                if (typeof callback === 'string') callback = callback.toFunc()

                var type = options.delType, opts = { url: options.delUrl, data: (options.delPK ? [{ name: options.delPK, value: postData.join(',') }] : JSON.stringify(postData)), type: 'POST', okCallback: callback || function (json) { delEnd() } }

                if (type && type === 'raw' && !options.delPK) {
                    opts.contentType = 'application/json'
                } else {
                    !options.delPK && (opts.data = { json: opts.data })
                    type && type !== 'raw' && (opts.type = type)
                }

                BJUI.ajax('doajax', opts)
            } else { // local delete
                delEnd()
            }
        }

        if (confirmMsg) {
            if (typeof confirmMsg !== 'string') confirmMsg = $trs.length == 1 ? BJUI.getRegional('datagrid.delMsg') : BJUI.getRegional('datagrid.delMsgM')

            that.$grid.alertmsg('confirm', confirmMsg, {
                okCall: function () {
                    doDel()
                }
            })
        } else {
            doDel()
        }
    }

    // inline edit
    Datagrid.prototype.inlineEdit = function ($tr, isAdd, data) {
        if (!this.tools.beforeEdit($tr, data)) {
            return false
        }

        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel, $tds = $tr.find('> td'), tds_length = $tds.length, tr_index = $tr.index(), data_index = tr_index

        data_index = tools.getNoChildDataIndex(data_index)

        if (!data) {
            if (that.isDom) data = $tr.data('initData') || tools.setDomData($tr)
            else data = that.data[data_index]
        }

        if ($tr.hasClass(that.classnames.tr_edit)) return false
        if (!that.inputs || !that.inputs.length) tools.initEditInputs()

        $tr.addClass(that.classnames.tr_edit).data(that.datanames.changeData, {})
        if ($tr.data('validator')) $tr.validator('destroy') //remove old validate

        if (!options.inlineEditMult) {
            that.doCancelEditRow($tr.siblings('.' + that.classnames.tr_edit))
        }

        that.$lastEditTr = $tr

        $tds.each(function (i) {
            var $td = $(this), op = columnModel[i], val = op && op.name && data[op.name], html = $td.html(), input = that.inputs[i], $input
            var onChange = function ($el, $td, val) {
                var changeData = $tr.data(that.datanames.changeData), jsontype = op.jsontype, defaultVal = (typeof op.defaultVal === 'undefined') ? null : op.defaultVal

                switch (op.type) {
                    case 'date':
                        $el
                            .change(function () {
                                $td.addClass(that.classnames.td_changed)
                                if ($el.val() == val) $td.removeClass(that.classnames.td_changed)
                                changeData[op.name] = $el.val()
                            })

                        break
                    case 'select':
                        $el.change(function () {
                            var value = $(this).val()

                            $td.addClass(that.classnames.td_changed)

                            if (value == String(val)) $td.removeClass(that.classnames.td_changed)

                            if ($el.prop('multiple')) {
                                $.isArray(value) && (value = value.join(','))
                            } else if (jsontype) {
                                if (jsontype === 'boolean') {
                                    value = Boolean(value)
                                } else if (jsontype === 'number') {
                                    if (value.length)
                                        !isNaN(Number(value)) && (value = Number(value))
                                    else
                                        value = null
                                }
                            }

                            changeData[op.name] = value
                        })

                        break
                    case 'boolean':
                        $el.on('ifChanged', function () {
                            $td.toggleClass(that.classnames.td_changed)

                            var checked = $(this).is(':checked')

                            if (checked == val)
                                $td.removeClass(that.classnames.td_changed)

                            changeData[op.name] = checked
                        })

                        break
                    case 'findgrid':
                        changeData[op.name] = $el.val()

                        $el.change(function () {
                            var value = $(this).val()

                            $td.addClass(that.classnames.td_changed)

                            if (value == val)
                                $td.removeClass(that.classnames.td_changed)
                            if (jsontype && jsontype === 'number') {
                                if (value.length)
                                    !isNaN(Number(value)) && (value = Number(value))
                                else
                                    value = null
                            }

                            changeData[op.name] = value
                        })

                        $td.off('afterchange.bjui.findgrid').on('afterchange.bjui.findgrid', '[data-toggle="findgrid"]', function (e, data) {
                            var include = op.attrs && op.attrs['data-options'] && op.attrs['data-options']['include']

                            if (include) {
                                $.each(include.split(','), function (i, n) {
                                    var obj = n.trim().split(':'), name = obj[0], key = obj.length > 1 ? obj[1] : obj[0]

                                    data['data'] && (changeData[name] = data['data'][key])
                                })
                            }
                        })

                        break
                    case 'spinner':
                        $el.change(function () {
                            $td.addClass(that.classnames.td_changed)
                            if ($el.val() == val) $td.removeClass(that.classnames.td_changed)
                            changeData[op.name] = Number($el.val())
                        })

                        break
                    default:
                        $el.change(function () {
                            var value = $(this).val()

                            $td.addClass(that.classnames.td_changed)

                            if (value == val)
                                $td.removeClass(that.classnames.td_changed)
                            if (jsontype && jsontype === 'number') {
                                if (value.length)
                                    !isNaN(Number(value)) && (value = Number(value))
                                else
                                    value = null
                            }

                            changeData[op.name] = value
                        })

                        break
                }

                if (isAdd) {
                    if (op.type === 'boolean') {
                        defaultVal = Boolean(defaultVal)
                        changeData[op.name] = defaultVal
                        $el.prop('checked', defaultVal)
                    } else {
                        if (defaultVal != null) {
                            if ($el.isTag('select') && $el.prop('multiple')) {
                                $el.val($.isArray(defaultVal) ? defaultVal : defaultVal.split(','))
                            } else {
                                $el.val(String(defaultVal))
                            }
                            if (jsontype) {
                                if (jsontype === 'number') {
                                    if (defaultVal.length)
                                        !isNaN(Number(defaultVal)) && (defaultVal = Number(defaultVal))
                                    else
                                        defaultVal = null
                                }
                                else if (jsontype === 'boolean')
                                    defaultVal = Boolean(defaultVal)
                            }

                            changeData[op.name] = defaultVal
                        } else {
                            if (($el.isTag('select') && !$el.prop('multiple'))) {
                                var $clone = $el.clone().appendTo('body'), val = $clone.val()

                                changeData[op.name] = val
                                $clone.remove()
                            }
                        }
                    }
                } else if (jsontype) {
                    $el.trigger('change')
                }
            }

            $td.data(that.datanames.td_html, html)

            if (isAdd) {
                if (!op.add) input = ''
            } else {
                if (data.addFlag) data.addFlag = false
                if (!op.edit) input = ''
            }

            if (input) {
                $input = $(input)

                if (typeof val === 'undefined' || val === 'null' || val === null)
                    val = ''

                if (op.type === 'boolean')
                    $input.prop('checked', val)
                else if (op.type === 'findgrid')
                    $input.data('context', $tr).val(String(val))
                else if (op.type === 'date')
                    if (val.length > 0)
                        $input.val(String(new Date(val).formatDate(op.pattern)))
                    else
                        $input.val(val);
                else {
                    $input.val(String(val))

                    if ($input.isTag('select') && $input.prop('multiple') && val && !$.isArray(val)) {
                        $input.val(val.split(','))
                    }
                }

                if (isAdd) {
                    if (op.addAttrs && typeof op.addAttrs === 'object') {
                        $.each(op.addAttrs, function (i, n) {
                            $input.attr(i, n)
                        })
                    }
                } else {
                    if (op.editAttrs && typeof op.editAttrs === 'object') {
                        $.each(op.editAttrs, function (i, n) {
                            $input.attr(i, n)
                        })
                    }
                }

                $td
                    .empty()
                    .append($input)
                    .addClass(that.classnames.td_edit)

                if (that.treeColumn && that.treeColumn === op) {
                    var treeInput = that.tools.createTreePlaceholder(data, '')

                    $td.attr('align', 'left').html(treeInput)

                    $input.appendTo($td.find('.datagrid-tree-title'))
                }

                onChange($input, $td, val)

                if (op.locked) {
                    var $lockTr = that.$lockTbody.find('tr:eq(' + tr_index + ')')
                    var $lockTd = $lockTr.find('> td:eq(' + op.lockIndex + ')')

                    $td.clone().html(html).insertAfter($td)
                    $td.show().insertAfter($lockTd).initui()
                    $lockTd.remove()
                }
            }

            if (!--tds_length) {
                $tr
                    .initui()
                    .validator({
                        msgClass: 'n-bottom',
                        theme: 'red_bottom_effect_grid'
                    })
            }
        })
    }

    Datagrid.prototype.saveAll = function ($trs) {
        var that = this, options = that.options, keys = options.keys, callback = options.editCallback, $tr, data, data_index, datas = [], changeData, tempData, postData = [], returnData = [], len = $trs && $trs.length

        if (!$trs || $trs.length) {
            $trs = that.$tbody.find('> tr.' + that.classnames.tr_edit)
            len = $trs.length
        }

        if (!len) return

        $trs.each(function () {
            $tr = $(this)

            data_index = that.tools.getNoChildDataIndex($tr.index())
            data = that.isDom ? $tr.data('initData') : that.data[data_index]
            datas.push(data)

            $tr.isValid(function (v) {
                if (v) {
                    // Update data
                    changeData = $tr.data(that.datanames.changeData)
                    $.extend(data, changeData)
                    // Specification post data
                    if (options.jsonPrefix) {
                        tempData = {}

                        $.each(data, function (name, value) {
                            if (!that.tools.isGridData(name))
                                tempData[options.jsonPrefix + '.' + name] = value
                        })
                    } else {
                        tempData = $.extend({}, data)

                        for (var key in keys)
                            delete tempData[keys[key]]
                    }

                    len--
                    postData.push(tempData)
                } else {
                    postData = []

                    return false
                }
            })
        })

        // do save
        if (!len) {
            // Callback
            if (callback) {
                callback = callback.toFunc()
            } else {
                callback = function (json) {
                    if ($.type(json) === 'array') {
                        returnData = json
                    } else if (typeof json === 'object') {
                        returnData.push(json)
                    }

                    $trs.each(function (i) {
                        $tr = $(this)
                        data_index = $tr.index()

                        if (that.columnModel[0] === that.childColumn)
                            data_index = data_index / 2

                        data = that.isDom ? $tr.data('initData') : that.data[data_index]

                        $.extend(data, typeof returnData[i] === 'object' && returnData[i])

                        if (that.allData && that.allData[data.gridIndex]) {
                            $.extend(that.allData[data.gridIndex], data)
                        }

                        // update allData for filter
                        if (that.oldAllData) that.oldAllData = that.allData.concat()

                        that.inlineEditComplete($tr, data)
                    })

                    that.tools.afterSave($trs, postData)
                }
            }
            if (that.options.saveLocal) {
                callback(postData)
            } else {
                // Do ajax
                if (that.tools.beforeSave($trs, datas)) {
                    var type = options.editType, opts = { url: options.editUrl, data: null, type: 'POST', okCallback: callback }

                    if (type && type === 'raw') {
                        opts.contentType = 'application/json'
                    } else {
                        if (that.inlineEditMult) {
                            opts.data = { json: JSON.stringify(postData) }
                        } else {
                            opts.data = postData[0];
                        }
                        type && (opts.type = type)
                    }

                    BJUI.ajax('doajax', opts)
                }
            }
        }
    }

    // update - inline edit
    Datagrid.prototype.updateEdit = function ($tr, $btn) {
        var that = this, options = that.options, keys = options.keys, callback = options.editCallback, data, datas = [], changeData, tempData, postData = [], returnData, data_index = $tr.index()

        data_index = that.tools.getNoChildDataIndex(data_index)

        if (that.isDom) data = $tr.data('initData')
        else data = that.data[data_index]

        if ($tr.hasClass(that.classnames.tr_edit)) {
            // validate
            $tr.isValid(function (v) {
                if (v) {
                    // Update data
                    changeData = $tr.data(that.datanames.changeData)
                    $.extend(data, changeData)
                    // Specification post data
                    if (options.jsonPrefix) {
                        tempData = {}
                        $.each(data, function (name, value) {
                            if (!that.tools.isGridData(name))
                                tempData[options.jsonPrefix + '.' + name] = value
                        })
                    } else {
                        tempData = $.extend({}, data)

                        for (var key in keys) {
                            delete tempData[keys[key]]
                        }
                    }
                    // Callback
                    if (callback) {
                        callback = callback.toFunc()
                    } else {
                        callback = function (json) {
                            if ($.type(json) === 'array') {
                                returnData = json[0]
                            } else if (typeof json === 'object') {
                                returnData = json
                            }

                            $.extend(data, typeof returnData === 'object' && returnData)

                            if (that.allData && that.allData[data.gridIndex]) {
                                $.extend(that.allData[data.gridIndex], data)
                            }

                            // update allData for filter
                            if (that.oldAllData) that.oldAllData = that.allData.concat()

                            that.inlineEditComplete($tr, data, $btn)
                            that.tools.afterSave($tr, data)
                        }
                    }
                    // Do ajax
                    datas.push(data)
                    if (that.tools.beforeSave($tr, datas)) {
                        postData.push(tempData)

                        var type = options.editType, opts = { url: options.editUrl, data: null, type: 'POST', okCallback: callback }

                        if (type && type === 'raw') {
                            opts.contentType = 'application/json'
                        } else {
                            if (that.inlineEditMult) {
                                opts.data = { json: JSON.stringify(postData) }
                            } else {
                                opts.data = postData[0];
                            }
                            type && (opts.type = type)
                        }

                        BJUI.ajax('doajax', opts)
                    }
                }
            })
        }
    }

    /* cancel - inline edit */
    Datagrid.prototype.cancelEdit = function ($trs) {
        var that = this, columnModel = that.columnModel

        $trs.each(function () {
            var $tr = $(this), tr_index = $tr.index(), data_index = tr_index

            if ($tr.hasClass(that.classnames.tr_edit)) {
                $tr
                    .removeClass(that.classnames.tr_edit)
                    .find('> td.' + that.classnames.td_edit).each(function () {
                        var $td = $(this), td_index = $td.index(), model = columnModel[td_index], html = $td.data(that.datanames.td_html)

                        $td.removeClass(that.classnames.td_edit).removeClass(that.classnames.td_changed).html(html)
                        if (model.locked) {
                            var $lockTr = that.$lockTbody.find('tr:eq(' + tr_index + ')')
                            var $lockTd = $lockTr.find('> td:eq(' + model.lockIndex + ')')

                            html = $lockTd.data(that.datanames.td_html)

                            $lockTr.removeClass(that.classnames.tr_edit)
                            $lockTd.removeClass(that.classnames.td_edit).removeClass(that.classnames.td_changed).html(html)
                        }
                    })
            }

            if ($tr.hasClass(that.classnames.tr_add)) {
                data_index = that.tools.getNoChildDataIndex(data_index)

                if (!that.isDom) {
                    that.allData = that.allData.remove(that.data[data_index].gridIndex)   // remove data in allData
                    that.data = that.data.remove(data_index)

                    that.tools.updateGridIndex()
                    that.$element.data('allData', that.allData)
                }

                if ($tr.next().hasClass(that.classnames.tr_child)) {
                    $tr.next().remove()
                    that.$lockTbody && that.$lockTbody.find('> tr:eq(' + tr_index + ')').next().remove()
                }

                that.$lockTbody && that.$lockTbody.find('> tr:eq(' + tr_index + ')').remove()
                $tr.remove()

                that.tools.createNoDataTr()

                if (that.options.height === 'auto')
                    that.fixedHeight()
            }
        })
    }

    // inline editComplete
    Datagrid.prototype.inlineEditComplete = function ($tr, trData, $btn) {
        var that = this, columnModel = that.columnModel, tr_index = $tr.index(), $tds = $tr.find('> td'), hasLinenumber = false, $lockTr = that.$lockTbody && that.$lockTbody.find('> tr:eq(' + tr_index + ')')
        var tdTemplate = that.options.tdTemplate, tempData = $.extend({}, trData)

        $.each(columnModel, function (i, n) {
            if (n === that.linenumberColumn) hasLinenumber = true
            if (that.tools.isGridModel(n)) return true

            var label = n.name ? trData[n.name] : '', render_label, $td = $tds.eq(n.index)

            if (that.isTemplate)
                $td = null
            if (typeof label === 'undefined' || label === 'null' || label === null)
                label = ''

            $td && ($td.text(label).removeClass(that.classnames.td_edit).removeClass(that.classnames.td_changed))

            if (n.render && typeof n.render === 'function') {
                if (n.items) {
                    render_label = n.render.call(that, label, trData, n.items, n.itemattr)
                    tempData[n.name] = render_label
                    $td && $td.html(render_label)
                } else {
                    render_label = n.render.call(that, label, trData)
                    tempData[n.name] = render_label
                    $td && $td.html(render_label)
                }
            } else if (n.items) {
                render_label = Datagrid.renderItem.call(that, label, trData, n.items, n.itemattr)
                tempData[n.name] = render_label
                $td && $td.html(render_label)
            }

            if (that.options.isTree && n.hasTree) {
                $td && ($td.attr('align', 'left').addClass('datagrid-tree-td').html(that.tools.createTreePlaceholder(trData, (render_label || label))))
            }

            if (n.locked && $lockTr) {
                var $lockTd = $lockTr.find('> td:eq(' + n.lockIndex + ')')

                $lockTd.removeClass(that.classnames.td_changed).html($td.html())
            }
        })

        if (that.isTemplate) {
            if (typeof tdTemplate === 'function')
                tdTemplate = tdTemplate.apply(that, [trData])

            tdTemplate = that.tools.replacePlh4Template(tdTemplate, tempData)

            $tr.find('> td.datagrid-template-td').html(tdTemplate)
        }

        $tr.removeClass(that.classnames.tr_edit).initui()
        if ($lockTr)
            $lockTr.removeClass(that.classnames.tr_edit).initui()

        if (!$btn) $btn = $tds.filter('.' + that.classnames.s_edit).find('button.update')
        if ($btn && $btn.length)
            $btn.trigger('bjui.datagrid.update.tr')

        if ($tr.hasClass(that.classnames.tr_add)) {
            $tr.removeClass(that.classnames.tr_add)
            $tr.find('> td.' + that.classnames.s_edit).removeData('isAdd')

            // update linenumber
            if (hasLinenumber) {
                that.tools.updateLinenumber()
            }

            // tree
            if (that.options.isTree) {
                var $parentTr = trData[that.options.keys.treePTr], parentData = trData[that.options.keys.treePData], keys = that.options.treeOptions.keys, $treePTd

                if ($parentTr && parentData) {
                    if (!parentData[keys.isParent]) {
                        parentData[keys.isParent] = true
                        $treePTd = $parentTr.find('> td.datagrid-tree-td')
                        $treePTd.html(that.tools.createTreePlaceholder(parentData, $treePTd.find('span.datagrid-tree-title').html()))
                    }
                    if (!parentData[keys.childLen]) {
                        parentData[keys.childLen] = 1
                    } else {
                        parentData[keys.childLen]++
                    }

                    $tr.data(that.options.keys.treePTr).data('child', parentData[keys.childLen]).attr('data-child', parentData[keys.childLen])
                }

                $tr.addClass('datagrid-tree-tr datagrid-tree-level-' + trData[keys.level]).attr('data-child', 0).attr('data-level', trData[keys.level])
            }

            // child
            $tr.next('.' + that.classnames.tr_child).remove()
            that.tools.createChildTr($tr, trData)
        }
    }

    // inline edit
    Datagrid.prototype.dialogEdit = function ($tr, isAdd, data) {
        if (!this.tools.beforeEdit($tr, data)) {
            return false
        }

        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel, tr_index = $tr.index(), data_index = tr_index, $dialog, $form, html = '', title

        if (!data) {
            data_index = tools.getNoChildDataIndex(data_index)

            if (that.isDom) data = $tr.data('initData') || tools.setDomData($tr)
            else data = that.data[data_index]
        }

        if (!that.inputs || !that.inputs.length) tools.initEditInputs()

        title = options.gridTitle || 'datagrid'

        if (isAdd) {
            title += ' - ' + BJUI.getRegional('datagrid.add')
        } else {
            if (data.addFlag) data.addFlag = false
            title += ' - ' + BJUI.getRegional('datagrid.edit')
        }

        $dialog = $('<div><div class="bjui-pageContent"></div><div class="bjui-pageFooter"></div></div>')
        $form = $('<form class="datagrid-dialog-edit-form"></form>')

        var onChange = function ($tr, $form, $el, model) {
            var changeData = $tr.data(that.datanames.changeData), jsontype = model.jsontype, defaultVal = (typeof model.defaultVal === 'undefined') ? null : model.defaultVal

            switch (model.type) {
                case 'date':
                    $el.change(function () {
                        changeData[model.name] = $(this).val()
                    })

                    break
                case 'select':
                    $el.change(function () {
                        var $element = $(this), val = $element.val(), multiple = $element.prop('multiple')

                        if (multiple && $.isArray(val)) {
                            val = val.join(',')
                        }
                        if (!multiple && jsontype) {
                            if (jsontype === 'boolean') {
                                val = Boolean(val)
                            } else if (jsontype === 'number') {
                                if (val.length)
                                    !isNaN(Number(val)) && (val = Number(val))
                                else
                                    val = null
                            }
                        }

                        changeData[model.name] = val
                    })

                    break
                case 'boolean':
                    $el.on('ifChanged', function () {
                        changeData[model.name] = $(this).is(':checked')
                    })

                    break
                case 'findgrid':
                    $el.change(function () {
                        var val = data.value

                        if (jsontype && jsontype === 'number') {
                            if (val.length)
                                !isNaN(Number(val)) && (val = Number(val))
                            else
                                val = null
                        }

                        changeData[model.name] = $(this).val()
                    })

                    $form.off('afterchange.bjui.findgrid').on('afterchange.bjui.findgrid', '[data-toggle="findgrid"]', function (e, data) {
                        var include = model.attrs && model.attrs['data-options'] && model.attrs['data-options']['include']

                        if (include) {
                            $.each(include.split(','), function (i, n) {
                                var obj = n.trim().split(':'), name = obj[0], key = obj.length > 1 ? obj[1] : obj[0]

                                data['data'] && (changeData[name] = data['data'][key])
                            })
                        }
                    })

                    break
                case 'spinner':
                    $el.change(function () {
                        changeData[model.name] = Number($(this).val())
                    })

                    break
                default:
                    $el.change(function () {
                        var val = $(this).val()

                        if (jsontype && jsontype === 'number') {
                            if (val.length)
                                !isNaN(Number(val)) && (val = Number(val))
                            else
                                val = null
                        }

                        changeData[model.name] = val
                    })

                    break
            }

            if (isAdd) {
                if (model.type === 'boolean') {
                    defaultVal = Boolean(defaultVal)
                    changeData[model.name] = defaultVal
                    $el.prop('checked', defaultVal)
                } else {
                    if (defaultVal != null) {
                        if ($el.isTag('select') && $el.prop('multiple')) {
                            $el.val($.isArray(defaultVal) ? defaultVal : defaultVal.split(','))
                        } else {
                            $el.val(String(defaultVal))
                        }
                        if (jsontype) {
                            if (jsontype === 'number')
                                !isNaN(Number(defaultVal)) && (defaultVal = Number(defaultVal))
                            else if (jsontype === 'boolean')
                                defaultVal = Boolean(defaultVal)
                        }

                        changeData[model.name] = defaultVal
                    } else {
                        //($el.isTag('select') && !$el.prop('multiple')) && (changeData[model.name] = $el.val())
                        if (($el.isTag('select') && !$el.prop('multiple'))) {
                            var $clone = $el.clone().appendTo('body'), val = $clone.val()

                            changeData[model.name] = val
                            $clone.remove()
                        }
                    }
                }
            } else if (jsontype) {
                $el.trigger('change')
            }
        }

        var onLoad = function ($dialog) {
            var $form = $dialog.find('form.datagrid-dialog-edit-form'),
                $btns = $dialog.find('div.bjui-pageFooter button'),
                $prev = $btns.first(),
                $next = $btns.eq(1),
                $cancel = $btns.eq(2),
                $save = $btns.last(),
                trindex, dataindex

            var createForm = function (data, $form, $tr) {
                $form.empty()
                if (!$tr.data(that.datanames.changeData)) $tr.data(that.datanames.changeData, {})

                if ($form.data('validator')) $form.validator('destroy')

                $.each(columnModel, function (i, n) {
                    if (!n.name || that.tools.isGridModel(n)) return true

                    var input = that.inputs[i], $input, $p = $('<p></p>'), id = 'datagrid-dialog-edit-column-' + i, val = data[n.name]

                    if (n.hide) $p.addClass('hide')
                    if (isAdd) {
                        if (!n.add) input = ''
                    } else {
                        if (!n.edit) input = ''
                    }

                    if (typeof val === 'undefined' || val === 'null' || val === null)
                        val = ''

                    if (input) {
                        $input = $(input).attr('id', id)
                        if ($input.isTag('select')) $input.attr('data-width', 'auto')
                        else if (!$input.isTag('checkbox')) $input.attr('size', 30)

                        if (n.type === 'boolean') {
                            $input.prop('checked', val)
                        } else if (n.type === 'findgrid') {
                            $input.val(String(val)).data('context', $form)
                        } else {
                            $input.val(String(val))
                        }

                        if ($input.isTag('select') && $input.prop('multiple') && val && !$.isArray(val)) {
                            $input.val(val.split(','))
                        }

                        if (isAdd) {
                            if (n.addAttrs && typeof n.addAttrs === 'object') {
                                $.each(n.addAttrs, function (k, v) {
                                    $input.attr(k, v)
                                })
                            }
                        } else {
                            if (n.editAttrs && typeof n.editAttrs === 'object') {
                                $.each(n.editAttrs, function (k, v) {
                                    $input.attr(k, v)
                                })
                            }
                        }
                    } else if (!isAdd) {
                        if (!n.edit)
                            input = val
                    }

                    $p
                        .append('<label class="control-label x120" for="' + id + '">' + n.label + '：</label>')
                        .append($('<span class="datagrid-dialog-column-span"></span>').append($input || input))
                        .appendTo($form)

                    if ($input)
                        onChange($tr, $form, $input, n)
                })

                $form
                    .initui()
                    .validator({
                        msgClass: 'n-bottom',
                        theme: 'red_bottom_effect_grid'
                    })
            }

            if ($form.is(':empty')) createForm(data, $form, $tr)

            trindex = $tr.index()

            if (that.columnModel[0] === that.childColumn)
                trindex = trindex * 2

            if (!trindex) $prev.addClass('disabled')
            if (trindex == that.data.length - 1) $next.addClass('disabled')

            $prev.click(function () {
                var $tr_prev = $tr.prev(), data

                if (that.options.hasChild && that.options.childOptions)
                    $tr_prev = $tr_prev.prev()
                if ($tr_prev.length) {
                    dataindex = that.tools.getNoChildDataIndex($tr_prev.index())

                    if (that.isDom) {
                        data = $tr_prev.data('initData') || tools.setDomData($tr_prev)
                    } else {
                        data = that.data[dataindex]
                    }

                    $tr = $tr_prev
                    createForm(data, $form, $tr)
                    $next.removeClass('disabled')

                    if (!$tr_prev.prev().length) $prev.addClass('disabled')
                } else {
                    $prev.addClass('disabled')
                }
            })

            $next.click(function () {
                var $tr_next = $tr.next(), data, $next_next

                if (that.options.hasChild && that.options.childOptions)
                    $tr_next = $tr_next.next()
                if ($tr_next.length) {
                    $next_next = $tr_next.next()
                    dataindex = that.tools.getNoChildDataIndex($tr_next.index())

                    if (that.options.hasChild && that.options.childOptions)
                        $next_next = $next_next.length && $next_next.next()
                    if (that.isDom) {
                        data = $tr_next.data('initData') || tools.setDomData($tr_next)
                    } else {
                        data = that.data[dataindex]
                    }

                    $tr = $tr_next
                    createForm(data, $form, $tr)
                    $form.initui()
                    $prev.removeClass('disabled')

                    if (!$next_next || !$next_next.length) $next.addClass('disabled')
                } else {
                    $next.addClass('disabled')
                }
            })

            $save.click(function () {
                var changeData, data, datas = [], postData, returnData, callback = options.editCallback

                dataindex = that.tools.getNoChildDataIndex($tr.index())

                if (that.isDom) data = $tr.data('initData')
                else data = that.data[dataindex]

                $form.isValid(function (v) {
                    if (v) {
                        changeData = $tr.data(that.datanames.changeData)
                        $.extend(data, changeData)

                        if (options.jsonPrefix) {
                            postData = {}
                            $.each(data, function (name, value) {
                                if (!that.tools.isGridData(name))
                                    postData[options.jsonPrefix + '.' + name] = value
                            })
                        } else {
                            postData = $.extend({}, data)

                            for (var key in that.options.keys)
                                delete postData[that.options.keys[key]]
                        }

                        // Callback
                        if (callback) {
                            callback = callback.toFunc()
                        } else {
                            callback = function (json) {
                                if ($.type(json) === 'array') {
                                    returnData = json[0]
                                } else if (typeof json === 'object') {
                                    returnData = json
                                }

                                $.extend(data, typeof returnData === 'object' && returnData)

                                that.dialogEditComplete($tr, data)
                                that.$grid.dialog('close', 'datagrid-dialog-edit')
                                that.tools.afterSave($tr, data)
                            }
                        }

                        // Do ajax
                        datas.push(data)
                        if (that.tools.beforeSave($tr, datas)) {
                            var type = options.editType, opts = { url: options.editUrl, data: null, type: 'POST', okCallback: callback }

                            if (type && type === 'raw') {
                                opts.contentType = 'application/json'
                            } else {
                                if (that.inlineEditMult) {
                                    opts.data = { json: JSON.stringify(postData) }
                                } else {
                                    opts.data = postData[0];
                                }
                                type && (opts.type = type)
                            }

                            BJUI.ajax('doajax', opts)
                        }
                    }
                })
            })

            $cancel.click(function () {
                that.$grid.dialog('close', 'datagrid-dialog-edit')
            })
        }

        var onClose = function () {
            var addRemove = false

            that.$tbody.find('> tr.' + that.classnames.tr_add).each(function () {
                var $tr = $(this), trindex = $tr.index(), dataindex = trindex

                dataindex = that.tools.getNoChildDataIndex(dataindex)

                that.data = that.data.remove(dataindex)

                if ($tr.next().hasClass(that.classnames.tr_child)) {
                    $tr.next().remove()
                    that.$lockTbody && that.$lockTbody.find('> tr:eq(' + trindex + ')').next().remove()
                }

                that.$lockTbody && that.$lockTbody.find('> tr:eq(' + trindex + ')').remove()
                $tr.remove()

                addRemove = true
            })

            if (addRemove && that.options.height === 'auto')
                that.fixedHeight()
        }

        $dialog.find('> div:first')
            .append($form)
            .next().append(BJUI.doRegional(FRAG.gridDialogEditBtns, that.regional))

        var dialog_options = $.extend({}, { id: 'datagrid-dialog-edit', fresh: true, target: $dialog[0], width: 520, height: 400, mask: true, title: title, onLoad: onLoad, onClose: onClose }, (typeof options.editDialogOp === 'object' && options.editDialogOp))

        that.$grid.dialog(dialog_options)
    }

    // dialog editComplete
    Datagrid.prototype.dialogEditComplete = function ($tr, trData) {
        var that = this, columnModel = that.columnModel, tr_index = $tr.index(), $tds = $tr.find('> td'), hasLinenumber = false, $trs = that.$tbody.find('> tr'), $lockTr = that.$lockTbody && that.$lockTbody.find('> tr:eq(' + tr_index + ')')
        var tdTemplate = that.options.tdTemplate, tempData = $.extend({}, trData), treeOptions = that.options.treeOptions, treePData, treePTr, treePTd

        $.each(columnModel, function (i, n) {
            if (n === that.linenumberColumn) hasLinenumber = true
            if (that.tools.isGridModel(n)) return true

            var label = n.name ? trData[n.name] : '', render_label, $td = $tds.eq(n.index)

            if (that.isTemplate)
                $td = null
            if (typeof label === 'undefined' || label === 'null' || label === null)
                label = ''

            $td && ($td.text(label).removeClass(that.classnames.td_edit).removeClass(that.classnames.td_changed))

            if (n.render && typeof n.render === 'function') {
                if (n.items) {
                    render_label = n.render.call(that, label, trData, n.items, n.itemattr)
                    tempData[n.name] = render_label
                    $td && $td.html(render_label)
                } else {
                    render_label = n.render.call(that, label, trData)
                    tempData[n.name] = render_label
                    $td && $td.html(render_label)
                }
            } else if (n.items) {
                render_label = Datagrid.renderItem.call(that, label, trData, n.items, n.itemattr)
                tempData[n.name] = render_label
                $td && $td.html(render_label)
            }

            if (that.options.isTree && n.hasTree) {
                $td && ($td.attr('align', 'left').addClass('datagrid-tree-td').html(that.tools.createTreePlaceholder(trData, (render_label || label))))
            }

            if (n.locked && $lockTr && $td) {
                var $lockTd = $lockTr.find('> td:eq(' + n.lockIndex + ')')

                $lockTd.removeClass(that.classnames.td_edit).removeClass(that.classnames.td_changed).html($td.html())
            }
        })

        if (that.isTemplate) {
            if (typeof tdTemplate === 'function')
                tdTemplate = tdTemplate.apply(that, [trData])

            tdTemplate = that.tools.replacePlh4Template(tdTemplate, tempData)

            $tr.find('> td.datagrid-template-td').html(tdTemplate)
        }

        $tr.initui()
        if ($lockTr)
            $lockTr.initui()

        if ($tr.hasClass(that.classnames.tr_add)) {
            $tr.removeClass(that.classnames.tr_add)

            // update linenumber
            if (hasLinenumber) {
                that.tools.updateLinenumber()
            }

            // tree
            if (that.options.isTree) {
                var $parentTr = trData[that.options.keys.treePTr], parentData = trData[that.options.keys.treePData], keys = that.options.treeOptions.keys, $treePTd

                if ($parentTr && parentData) {
                    if (!parentData[keys.isParent]) {
                        parentData[keys.isParent] = true
                        $treePTd = $parentTr.find('> td.datagrid-tree-td')
                        $treePTd.html(that.tools.createTreePlaceholder(parentData, $treePTd.find('span.datagrid-tree-title').html()))
                    }
                    if (!parentData[keys.childLen]) {
                        parentData[keys.childLen] = 1
                    } else {
                        parentData[keys.childLen]++
                    }

                    $tr.data(that.options.keys.treePTr).data('child', parentData[keys.childLen]).attr('data-child', parentData[keys.childLen])
                }

                $tr.addClass('datagrid-tree-tr datagrid-tree-level-' + trData[keys.level]).attr('data-child', 0).attr('data-level', trData[keys.level])
            }

            // child
            $tr.next('.' + that.classnames.tr_child).remove()
            that.tools.createChildTr($tr, trData)
        }
    }

    /* resize */
    Datagrid.prototype.resizeGrid = function () {
        var that = this, $target = that.$grid.getPageTarget(), parentW, parentH
        var _resizeGrid = function () {
            var ww = that.$grid.width(), $headDiv = that.$tableH.next('.datagrid-thead-dialog-div'),
                newTemplate = ((that.options.tdTemplate && that.options.templateWidth) && that.options.templateWidth > ww) || that.options.templateWidth === 0

            // tdtemplate
            if (newTemplate !== that.isTemplate) {
                that.isTemplate = newTemplate
                that.tools.coverTemplate()
                return
            }
            if ((that.options.dialogFilterW && ww < that.options.dialogFilterW)
                || that.options.dialogFilterW === 0) {

                that.$tableH.hide()

                if (!$headDiv.length) {
                    that.$tableH.after('<div class="datagrid-thead-dialog-div" style="padding:5px;"><button type="button" class="btn btn-orange datagrid-thead-dialog-view">' + BJUI.getRegional('datagrid.fAndS') + '</button><span class="datagrid-thead-dialog-filter-msg"><span class="msg-sort"></span><span class="msg-filter"></span></span></div>')
                } else {
                    $headDiv.show()
                }

                if (!that.$headFilterUl)
                    that.filterInThead(true)

                that.$grid.off('click.datagrid.thead.view').on('click.datagrid.thead.view', '.datagrid-thead-dialog-view', function (e) {
                    BJUI.dialog({
                        id: 'datagrid-thead-view', html: '<div class="bjui-pageContent"></div><div class="bjui-pageFooter"><ul><li><button type="button" class="btn btn-close">关闭</button></li></ul></div>', width: 360, height: 300, title: 'datagrid - thead - columns',
                        onLoad: function ($dialog) {
                            that.$headFilterUl.show().appendTo($dialog.find('.bjui-pageContent'))
                        },
                        beforeClose: function ($dialog) {
                            that.$headFilterUl.hide().appendTo(that.$grid)
                            return true
                        }
                    })
                })
            } else {
                that.$grid.removeClass('datagrid-flowlayout')
                that.$tableH.show()
                $headDiv.hide()

                if (that.$colgroupB.is(':hidden')) {
                    that.$colgroupB.show()
                }
            }

            if (that.initFixedW && String(that.options.width).endsWith('%')) {
                parentW = that.$grid.parent().width()
                that.fixedWidth()

                if (that.options.hasChild && that.options.childOptions) {
                    that.$tbody.find('> tr.' + that.classnames.tr_child + ':visible').each(function () {
                        var $child = $(this), $tr = $child.prev(), $table = $tr.data('bjui.datagrid.child')

                        if ($table && $table.length) {
                            $table.datagrid('fixedWidth')
                        }
                    })
                }
            }

            if (String(that.options.height).endsWith('%')) {
                that.tools.setBoxbH()
            }
        }

        // for tab
        $('a[data-toggle="tab"]').on('shown.bs.tab', $.proxy(function (e) {
            if (!that.$element.data('bjui.datagrid.init.tab')) {
                var $target = $(e.target), $box = $target.data('target'), href = $target.attr('href')

                if (!$box)
                    $box = $(href)

                if ($box && $box.length) {
                    if ($box.find(that.$element).length) {
                        that.$element.data('bjui.datagrid.init.tab', true)
                        that.$element.datagrid('fixedHeight')
                    }
                }
            }
        }, that))

        $(window).on(BJUI.eventType.resizeGrid, $.proxy(_resizeGrid, that))
    }


    // DATAGRID PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments,
            property = option

        return this.each(function () {
            var $this = $(this),
                options = $.extend(true, {}, Datagrid.DEFAULTS, typeof option === 'object' && option),
                data = $this.data('bjui.datagrid')

            if (!data) $this.data('bjui.datagrid', (data = new Datagrid(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.datagrid

    $.fn.datagrid = Plugin
    //$.fn.datagrid.Constructor = Datagrid
    $.datagrid = Datagrid

    // DATAGRID NO CONFLICT
    // =================

    $.fn.datagrid.noConflict = function () {
        $.fn.datagrid = old
        return this
    }

    // DATAGRID DATA-API
    // ==============

    $(document).on(BJUI.eventType.initUI, function (e) {
        $(e.target).find('table[data-toggle="datagrid"]').each(function () {
            var $this = $(this), options = $this.data()

            if (!$this.length) return

            if (options.options && typeof options.options === 'string') options.options = options.options.toObj()
            $.extend(options, typeof options.options === 'object' && options.options)

            Plugin.call($this, options)
        })
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-spinner.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-spinner.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // SPINNER CLASS DEFINITION
    // ======================
    var Spinner = function (element, options) {
        this.$element = $(element)
        this.options = options
        this.tools = this.TOOLS()
        this.$spinner = null
        this.height = this.$element.addClass('form-control').innerHeight()
        this.ivalue = Number(this.$element.val()) || 0
    }

    Spinner.DEFAULTS = {
        min: 0,
        max: 100,
        step: 1,
        decimalPlace: 0
    }

    Spinner.EVENTS = {
        afterChange: 'afterchange.bjui.spinner'
    }

    Spinner.prototype.TOOLS = function () {
        var that = this
        var tools = {
            changeVal: function ($btn) {
                var $input = that.$element
                var ivalue = Number($input.val()) || Number(that.ivalue)
                var type = $btn.data('add') || -1
                var istart = that.options.min, iend = that.options.max, istep = that.options.step

                if (type == 1) {
                    if (ivalue <= iend - istep)
                        ivalue = ivalue + istep
                } else if (type == -1) {
                    if (ivalue >= (istart + istep))
                        ivalue = ivalue - istep
                } else if (ivalue > iend) {
                    ivalue = iend
                } else if (ivalue < istart) {
                    ivalue = istart
                }
                if (that.options.decimalPlace)
                    ivalue = new String(ivalue.toFixed(that.options.decimalPlace))

                that.ivalue = ivalue

                $input
                    .val(ivalue)
                    .trigger(Spinner.EVENTS.afterChange, { value: ivalue })
                    .trigger('change')
            }
        }

        return tools
    }

    Spinner.prototype.init = function () {
        var that = this
        var $element = this.$element
        var options = this.options

        if (isNaN(this.options.min) || isNaN(this.options.max) || isNaN(this.options.step)) {
            BJUI.debug('BJUI.Spinner: Parameter is non-numeric type!')
            return
        }

        this.addBtn()
    }

    Spinner.prototype.addBtn = function () {
        var that = this, $element = that.$element, fluid = $element.attr('size') ? '' : ' fluid'

        if (!this.$lookBtn && !$element.parent().hasClass('wrap_bjui_btn_box')) {
            this.$spinner = $(FRAG.spinnerBtn)

            $element.css({ 'paddingRight': '13px' }).wrap('<span class="wrap_bjui_btn_box' + fluid + '"></span>')

            var $box = $element.parent()

            $box.css('position', 'relative')
            this.$spinner.css({ 'height': this.height }).appendTo($box)
            this.$spinner.on('selectstart', function () { return false })

            var timer = null

            that.$spinner.find('li').on('click', function (e) {
                that.tools.changeVal($(this))
            }).on('mousedown', function () {
                var $btn = $(this)

                timer = setInterval(function () {
                    that.tools.changeVal($btn)
                }, 150)
            }).on('mouseup', function () { clearTimeout(timer) })
        }
    }

    Spinner.prototype.destroy = function () {
        if (this.$element.parent().hasClass('wrap_bjui_btn_box')) {
            this.$element.parent().find('.bjui-spinner').remove()
            this.$element.unwrap()
        }
    }

    // SPINNER PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments
        var property = option

        return this.each(function () {
            var $this = $(this)
            var options = $.extend({}, Spinner.DEFAULTS, $this.data(), typeof option === 'object' && option)
            var data = $this.data('bjui.spinner')

            if (!data) $this.data('bjui.spinner', (data = new Spinner(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.spinner

    $.fn.spinner = Plugin
    $.fn.spinner.Constructor = Spinner

    // SPINNER NO CONFLICT
    // =================

    $.fn.spinner.noConflict = function () {
        $.fn.spinner = old
        return this
    }

    // SPINNER DATA-API
    // ==============

    $(document).on(BJUI.eventType.initUI, function (e) {
        var $this = $(e.target).find('input[data-toggle="spinner"]')

        if (!$this.length) return

        Plugin.call($this)
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-findgrid.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-findgrid.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // FINDGRID GLOBAL ELEMENTS
    // ======================

    var group, suffix, pk, include, exclude, append, oldData, beforeSelect, onSelect, afterSelect, $box, $currentFindGrid

    // FINDGRID CLASS DEFINITION
    // ======================

    var FindGrid = function (element, options) {
        this.$element = $(element)
        this.options = options
        this.$findBtn = null
    }

    FindGrid.DEFAULTS = {
        pk: null,
        oldData: null,
        group: null,
        suffix: null,
        include: null,
        exclude: null,
        multiple: false,
        append: false,
        empty: true,
        dialogOptions: {
            id: null,
            mask: true,
            width: 600,
            height: 400,
            title: 'FindGrid',
            maxable: true,
            resizable: true
        },
        gridOptions: {
            width: '100%',
            height: '100%',
            tableWidth: '100%',
            columnMenu: false
        },
        context: null,
        beforeSelect: null,
        onSelect: null,
        afterSelect: null
    }

    FindGrid.EVENTS = {
        afterChange: 'afterchange.bjui.findgrid'
    }

    FindGrid.getField = function (key) {
        return (group ? (group + '.') : '') + (key) + (suffix ? suffix : '')
    }

    FindGrid.empty = function () {
        if (!include) {
            BJUI.debug('BJUI.FindGrid: No set options \'include\' !')
            return
        }

        var that = this, includes = include.split(','), $inputs = $box.find(':text, :input:hidden, textarea, select'), includeKeys = {}

        for (var i = 0; i < includes.length; i++) {
            var arr = includes[i].split(':'), key, fieldKey

            if (arr.length == 2) {
                fieldKey = arr[0].trim()
                key = arr[1].trim()
            } else {
                fieldKey = includes[i].trim()
                key = fieldKey
            }

            includeKeys[that.getField(fieldKey)] = true
        }

        for (key in includeKeys) {
            $inputs.filter('[name="' + key + '"]')
                .val('')
                .trigger(FindGrid.EVENTS.afterChange, { value: '', data: '' })
                .trigger('focus')
                .trigger('change')
        }
    }

    FindGrid.setSingle = function (data) {
        if (typeof onSelect === 'function') {
            onSelect.call(this, data)
            return
        }

        var that = this
        var setVal = function ($input, fieldKey, value) {
            var name = that.getField(fieldKey)

            if (name == $input.attr('name')) {
                $input
                    .val(value)
                    .trigger(FindGrid.EVENTS.afterChange, { value: value, data: data })
                    .trigger('focus')
                    .trigger('change')

                if ($input.isTag('select') && $input.data('toggle') && $input.data('toggle') === 'selectpicker') {
                    $input.selectpicker('refresh')
                }
            }
        }

        $box.find('input:text, input:hidden, textarea, select').each(function () {
            var $input = $(this), fieldKey, excludeKeys = []

            if (include) {
                var includes = include.split(',')

                for (var i = 0; i < includes.length; i++) {
                    var arr = includes[i].split(':'), key

                    if (arr.length == 2) {
                        fieldKey = arr[0].trim()
                        key = arr[1].trim()
                    } else {
                        fieldKey = includes[i].trim()
                        key = fieldKey
                    }

                    if (data && typeof data[key] !== 'undefined')
                        setVal($input, fieldKey, data[key])
                }
            } else {
                for (var key in data) {
                    if (exclude) {
                        $.each(exclude.split(','), function (i, n) {
                            excludeKeys.push(n.trim())
                        })

                        if ($.inArray(key, excludeKeys) != -1) {
                            continue
                        }
                    }

                    setVal($input, key, data[key])
                }
            }
        })

        if (typeof afterSelect === 'function') {
            afterSelect.call(that, data)
            return
        }
    }

    FindGrid.setMult = function (gridId) {
        var datas = $('#' + gridId).data('selectedDatas')

        if (typeof beforeSelect === 'function') {
            if (!beforeSelect.apply(that, [datas])) {
                return false
            }
        }
        if (typeof onSelect === 'function') {
            onSelect.call(that, datas)
            return
        }

        var that = this, data, inputLen = 0, newVal
        var refreshSelect = function ($input) {
            if ($input.isTag('select') && $input.data('toggle') && $input.data('toggle') === 'selectpicker') {
                $input.selectpicker('refresh')
            }
        }

        if (datas && datas.length) {
            var $inputs = $box.find('input:text, input:hidden, textarea, select'), fieldKey, includeKeys = {}, okObj = {}, excludeKeys = [], v

            if (!append && oldData)
                oldData = []

            if (include) {
                var includes = include.split(',')

                for (var i = 0; i < includes.length; i++) {
                    var arr = includes[i].split(':'), key, obj

                    if (arr.length == 2) {
                        fieldKey = arr[0].trim()
                        key = arr[1].trim()
                    } else {
                        fieldKey = includes[i].trim()
                        key = fieldKey
                    }

                    includeKeys[that.getField(fieldKey)] = key
                }
            } else {
                for (var key in datas[0]) {
                    if (exclude) {
                        $.each(exclude.split(','), function (i, n) {
                            excludeKeys.push(n.trim())
                        })
                        if ($.inArray(key, excludeKeys) === -1) {
                            includeKeys[that.getField(key)] = key
                        }
                    } else {
                        includeKeys[that.getField(key)] = key
                    }
                }
            }

            $inputs = $($.map($inputs, function (n) {
                var $n = $(n), name = $n.attr('name')

                if (!name) return null
                if (!includeKeys[name]) return null

                okObj[name] = includeKeys[name]

                return n
            }))

            for (var j = 0; j < datas.length; j++) {
                data = datas[j]

                if (oldData && $.inArray(data[pk], oldData) != -1 && !append)
                    continue

                $inputs.each(function (k) {
                    var $input = $(this), name = $input.attr('name'), value = $input.val()

                    if (!newVal) newVal = new Array()
                    if (!newVal[k]) newVal[k] = []

                    if (!append) {
                        $input.val('')
                        refreshSelect($input)
                    } else {
                        if (value) {
                            newVal[k].push(value)

                            if ($input.isTag('select') && $input.prop('multiple')) {
                                newVal[k] = $.type(value) === 'array' ? value : []
                            }
                        }
                    }

                    newVal[k].push(data[okObj[name]])

                    if (oldData && $.inArray(data[pk], oldData) === -1)
                        oldData.push(data[pk])
                })
            }

            if (newVal) {
                $inputs.each(function (k) {
                    var $input = $(this)

                    v = newVal[k].join(',')

                    if ($input.isTag('select') && $input.prop('multiple'))
                        v = newVal[k]

                    $input
                        .val(v)
                        .trigger(FindGrid.EVENTS.afterChange, { value: v, data: datas })
                        .trigger('change')

                    $currentFindGrid.data('oldData', oldData)

                    refreshSelect($input)
                })
            }

            if (typeof afterSelect === 'function') {
                afterSelect.call(that, data.data)
                return
            }

            BJUI.dialog('closeCurrent')
        }
    }

    FindGrid.prototype.init = function () {
        var that = this, options = this.options, gridOptions = options.gridOptions, tools = this.tools

        group = options.group || null
        suffix = options.suffix || null
        $currentFindGrid = that.$element
        oldData = options.oldData
        pk = options.pk || null
        include = options.include || null
        exclude = options.exclude || null
        $box = null

        if (options.context) {
            if (options.context instanceof jQuery)
                $box = options.context
            else
                $box = $(options.context)
        }
        if (!$box || !$box.length) {
            if (that.$element[0] === $('body')[0]) {
                $box = $.CurrentDialog || $.CurrentNavtab
            } else {
                $box = that.$element.closest('.unitBox')
            }
        }
        if (suffix)
            suffix = suffix.trim()

        if (pk) {
            if ($currentFindGrid.data('oldData')) {
                oldData = $currentFindGrid.data('oldData')
            } else {
                if (typeof oldData !== 'undefined') {
                    if (typeof oldData === 'string')
                        oldData = oldData.split(',')
                    else if (!$.isArray(oldData))
                        oldData = []
                } else {
                    oldData = []
                }
            }
        } else {
            oldData = null
        }

        if (gridOptions.dataUrl) {
            gridOptions.dataUrl = decodeURI(gridOptions.dataUrl).replacePlh($box)

            if (!gridOptions.dataUrl.isFinishedTm()) {
                BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('Findgrid Plugin: gridOptions -> dataUrl is incorrect: ' + gridOptions.dataUrl)
                return
            }

            gridOptions.dataUrl = encodeURI(gridOptions.dataUrl)
        }

        this.open(that.$element)
    }

    FindGrid.prototype.addBtn = function () {
        var that = this, $element = that.$element, options = $element.data('bjui.findgrid.options'), fluid = $element.attr('size') ? '' : ' fluid'

        if (!this.$findBtn && !$element.parent().hasClass('wrap_bjui_btn_box')) {
            this.$findBtn = $(FRAG.findgridBtn)
            this.$element.css({ 'paddingRight': '15px' }).wrap('<span class="wrap_bjui_btn_box' + fluid + '"></span>')

            var $box = this.$element.parent()
            var height = this.$element.addClass('form-control').innerHeight()

            $box.css({ 'position': 'relative', 'display': 'inline-block' })

            delete options.toggle

            that.$findBtn.data('bjui.findgrid.options', options)

            this.$findBtn.css({ 'height': height, 'lineHeight': height + 'px' }).appendTo($box)
            this.$findBtn.on('selectstart', function () { return false })
        }
    }

    FindGrid.prototype.open = function ($obj) {
        var that = this, options = this.options, dialogOptions = options.dialogOptions, gridOptions = options.gridOptions, timestamp = (new Date().getTime())

        if (!dialogOptions.id)
            dialogOptions.id = 'dialog_findgrid_' + timestamp
        if (!options.gridId)
            options.gridId = 'datagrid_findgrid_' + timestamp

        gridOptions.showToolbar = false

        if (options.empty) {
            if (!gridOptions.toolbarCustom) gridOptions.toolbarCustom = ''
            gridOptions.showToolbar = true
        }
        if (options.multiple) {
            gridOptions.showToolbar = true

            if (typeof gridOptions.showCheckboxcol === 'undefined') {
                gridOptions.showCheckboxcol = true
            }
            if (!gridOptions.toolbarCustom) gridOptions.toolbarCustom = ''

            gridOptions.selectMult = true

            // set multiple
            gridOptions.toolbarCustom += '　<button type="button" class="btn-blue" onclick="$.fn.findgrid.Constructor.setMult(\'' + options.gridId + '\')">' + (BJUI.getRegional('findgrid.choose')) + '</button>'
            gridOptions.toolbarCustom += '　<label class="ilabel"><input type="checkbox" data-toggle="icheck" id="checkbox_findgrid_' + timestamp + '" ' + (options.append ? 'checked' : '') + '>&nbsp;' + (BJUI.getRegional('findgrid.append')) + '</label>'

            append = options.append
        }

        if (options.empty)
            gridOptions.toolbarCustom += '　<button type="button" class="btn-orange" onclick="$.fn.findgrid.Constructor.empty(\'' + options.gridId + '\')">' + (BJUI.getRegional('findgrid.empty')) + '</button>'

        delete dialogOptions.url
        delete dialogOptions.target

        if (options.onSelect) {
            onSelect = options.onSelect
            if (typeof onSelect === 'string') onSelect = onSelect.toFunc()
        } else {
            onSelect = null
        }
        if (options.beforeSelect) {
            beforeSelect = options.beforeSelect
            if (typeof beforeSelect === 'string') beforeSelect = beforeSelect.toFunc()
        } else {
            beforeSelect = null
        }
        if (options.afterSelect) {
            afterSelect = options.afterSelect
            if (typeof afterSelect === 'string') afterSelect = afterSelect.toFunc()
        } else {
            afterSelect = null
        }

        dialogOptions.html = '<div class="bjui-pageContent tableContent"><table id="' + options.gridId + '"></table></div>'
        dialogOptions.onLoad = function ($dialog) {
            var gridId = '#' + options.gridId, $grid = $(gridId)

            $grid.datagrid(gridOptions)

            // after load - selected row by (oldData && pk)
            if ($.isArray(oldData) && oldData.length && pk) {
                $(document).on('afterLoad.bjui.datagrid', gridId, function (e, data) {
                    if (data.datas) {
                        $.each(data.datas, function (i, data) {
                            if (data[pk]) {
                                if ($.inArray(data[pk], oldData) != '-1') {
                                    $grid.datagrid('selectedRows', i, true)
                                }
                            }
                        })
                    }
                })
            }

            // set single
            if (!options.multiple) {
                $(document).on('clicked.bjui.datagrid.tr', gridId, function (e, data) {
                    if (typeof beforeSelect === 'function') {
                        if (!beforeSelect.apply(that, [data.data])) {
                            return false
                        }
                    }
                    if (typeof onSelect === 'function') {
                        onSelect.call(that, data.data)
                    } else {
                        FindGrid.setSingle(data.data)
                        BJUI.dialog('closeCurrent')
                    }
                })
            }

            // append checkbox
            $(document).on('ifChanged', '#checkbox_findgrid_' + timestamp, function (e) {
                append = $(this).is(':checked')
            })
        }

        BJUI.dialog(dialogOptions)
    }

    // FINDGRID PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments
        var property = option

        return this.each(function () {
            var $this = $(this),
                options = {},
                data

            $.extend(true, options, FindGrid.DEFAULTS, typeof option === 'object' && option)

            data = new FindGrid(this, options)

            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.findgrid

    $.fn.findgrid = Plugin
    $.fn.findgrid.Constructor = FindGrid

    // FINDGRID NO CONFLICT
    // =================

    $.fn.findgrid.noConflict = function () {
        $.fn.findgrid = old
        return this
    }

    // NOT SELECTOR
    // ==============

    BJUI.findgrid = function (option) {
        Plugin.apply($('body'), [option])
    }

    // FINDGRID DATA-API
    // ==============

    $(document).on(BJUI.eventType.initUI, function (e) {
        $(e.target).find('input[data-toggle="findgrid"]').each(function () {
            var $this = $(this), data = $this.data(), options = data.options

            if (options) {
                if (typeof options === 'string') {
                    if (options.trim().startsWith('{'))
                        options = options.toObj()
                    else
                        options = options.toFunc()
                }
                if (typeof options === 'function') {
                    options = options.apply()
                }
                if (typeof options === 'object') {
                    delete data.options

                    $.extend(data, options)
                }
            }

            $this.data('bjui.findgrid.options', data)
            Plugin.call($this, 'addBtn')
        })
    })

    $(document).on('click.bjui.findgrid.data-api', '[data-toggle="findgridbtn"]', function (e) {
        var $this = $(this), opts = $this.data('bjui.findgrid.options')

        if (!opts) {
            var data = $this.data(), options = data.options

            if (options) {
                if (typeof options === 'string') {
                    if (options.trim().startsWith('{'))
                        options = options.toObj()
                    else
                        options = options.toFunc()
                }
                if (typeof options === 'function') {
                    options = options.apply()
                }
                if (typeof options === 'object') {
                    delete data.options

                    $.extend(data, options)
                }
            }
            opts = data
        }

        delete opts['bjui.findgrid.options']

        Plugin.call($this, opts)

        e.preventDefault()
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-suggest.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-suggest.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // FINDGRID GLOBAL ELEMENTS
    // ======================

    var group, suffix, keys, include, autofill, onSelect, $box, $currentSuggest

    // FINDGRID CLASS DEFINITION
    // ======================

    var Suggest = function (element, options) {
        this.$element = $(element)
        this.options = options
        this.$findBtn = null
    }

    Suggest.DEFAULTS = {
        keys: null,
        group: null,
        suffix: null,
        include: null,
        context: null,
        eventType: 'change',
        autofill: false
    }

    Suggest.EVENTS = {
        afterChange: 'afterchange.bjui.suggest'
    }

    Suggest.getField = function (key) {
        return (group ? (group + '.') : '') + (key) + (suffix ? suffix : '')
    }

    Suggest.setSingle = function (datas) {
        var that = this, items, menus = [], $parents, inputs = []
        var replacePlh = function (val, data) {
            return val.replace(/#\/?[^#]*#/g, function ($1) {
                var key = $1.replace(/[##]+/g, ''), val = data[key]

                if (typeof val === 'undefined')
                    return $1

                if (typeof val === 'undefined' || val === 'null' || val === null)
                    val = ''

                return val
            })
        }
        var createMenu = function ($input, value, label) {
            items = []

            var $suggest = $input.data('suggest.menu'), val, lab

            if (!$suggest || !$suggest.length) {
                $suggest = $('<ul class="bjui-suggest-menu"></ul>')
            }

            $.each(datas, function (i, n) {
                val = value
                lab = label
                val = replacePlh(val, n)
                lab = replacePlh(lab, n)

                if (val || lab)
                    items.push('<li data-value="' + val + '">' + lab + '</li>')
            })

            if (items.length) {
                $suggest
                    .html(items.join(''))
                    .appendTo('body').css({ position: 'absolute', top: $input.offset().top + $input.outerHeight(), left: $input.offset().left })
                    .on('click.bjui.suggest', 'li', function () {
                        $input.val($(this).attr('data-value'))
                    })
                    .on('mouseover.bjui.suggest', 'li', function () {
                        $(this).addClass('menu-highlight')
                    })
                    .on('mouseout.bjui.suggest', 'li', function () {
                        $(this).removeClass('menu-highlight')
                    })

                $input.addClass('bjui-suggest-input').attr('autocomplete', false).data('suggest.menu', $suggest).off('focus.bjui.suggest').on('focus.bjui.suggest', function () {
                    $suggest.css({ top: $input.offset().top + $input.outerHeight(), left: $input.offset().left }).show()
                }).off('destroy.bjui.suggest').on('destroy.bjui.suggest', function () {
                    $suggest.remove()
                })

                menus.push($suggest)
            }
        }

        $.each(keys, function (k, v) {
            var name = that.getField(k), $input = $box.find('input[name="' + name + '"], select[name="' + name + '"], textarea[name="' + name + '"]'), arr = v ? v.split('/') : ['#' + k + '#'], item, value, label

            if ($input && $input.length) {
                if (arr.length > 1) {
                    value = arr[0]
                    label = arr[1]
                } else {
                    value = label = arr[0]
                }

                if (($input.isTag('select') || $input.isTag('textarea')) && autofill) {
                    $input.val(datas[0] ? datas[0][k] : '')
                    if ($input.isTag('select')) {
                        $input.selectpicker('refresh')
                    }
                }
                else {
                    createMenu($input, value, label)
                    if (autofill)
                        $input.val(datas[0] ? datas[0][k] : '')
                }

                inputs.push($input)

                if (!$parents)
                    $parents = $input.parents()
            }
        })

        $(document).on('click.bjui.suggest', function (e) {
            var $suggest = $(e.target).data('suggest.menu')

            if (!$suggest || !$suggest.length)
                $('body').find('> .bjui-suggest-menu').hide()
            else
                $suggest.siblings('.bjui-suggest-menu').hide()
        })

        $parents.on('scroll.bjui.suggest', function () {
            $.each(inputs, function (i, n) {
                var $suggest = n.data('suggest.menu')

                if ($suggest && $suggest.is(':visible')) {
                    $suggest.css({ top: n.offset().top + n.outerHeight(), left: n.offset().left })
                }
            })
        })

        $currentSuggest.data('menus', menus)
    }

    Suggest.prototype.init = function () {
        var that = this, options = this.options, tools = this.tools

        keys = options.keys || null
        group = options.group || null
        suffix = options.suffix || null
        autofill = options.autofill || false
        $currentSuggest = that.$element
        $box = null

        if (options.context) {
            if (options.context instanceof jQuery)
                $box = options.context
            else
                $box = $(options.context)
        }
        if (!$box || !$box.length)
            $box = that.$element.closest('.unitBox')
        if (suffix)
            suffix = suffix.trim()

        if (!options.keys) {
            BJUI.debug('BJUI.Suggest: options -> keys is not defined!')
            return
        }
        if (!options.url) {
            BJUI.debug('BJUI.Suggest: options -> url is not defined!')
            return
        } else {
            options.url = decodeURI(options.url).replacePlh($box)

            if (!options.url.isFinishedTm()) {
                BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('BJUI.Suggest: options -> url is incorrect: ' + options.url)
                return
            }

            options.url = encodeURI(options.url)
        }

        that.$element.on(options.eventType || 'change', function () {
            var val = $(this).val()

            if (val)
                that.load(val)
        })
    }

    Suggest.prototype.load = function (value) {
        var that = this, options = this.options, url = options.url

        if (url.indexOf('#val#') != -1) {
            url = url.replaceAll('#val#', value)
        }

        BJUI.ajax('doajax', {
            url: url,
            data: { value: value },
            type: options.type,
            cache: options.cache,
            okCallback: function (json) {
                Suggest.setSingle(json)
            }
        })
    }

    Suggest.prototype.destroyMenu = function () {
        var menus = $currentSuggest.data('menus') || []

        if (menus.length) {
            $.each(menus, function (i, n) {
                n.remove()
            })
        }
    }

    // FINDGRID PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments
        var property = option

        return this.each(function () {
            var $this = $(this),
                options = {},
                data

            $.extend(true, options, Suggest.DEFAULTS, typeof option === 'object' && option)

            data = new Suggest(this, options)

            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.suggest

    $.fn.suggest = Plugin
    $.fn.suggest.Constructor = Suggest

    // FINDGRID NO CONFLICT
    // =================

    $.fn.suggest.noConflict = function () {
        $.fn.suggest = old
        return this
    }

    // FINDGRID DATA-API
    // ==============

    $(document).on(BJUI.eventType.initUI, function (e) {
        $(e.target).find('input[data-toggle="suggest"]').each(function () {
            var $this = $(this), data = $this.data(), options = data.options

            if (options) {
                if (typeof options === 'string') {
                    if (options.trim().startsWith('{'))
                        options = options.toObj()
                    else
                        options = options.toFunc()
                }
                if (typeof options === 'function') {
                    options = options.apply()
                }
                if (typeof options === 'object') {
                    delete data.options

                    $.extend(data, options)
                }
            }

            Plugin.call($this, data)
        })
    })

    $(document).on('click.bjui.suggest.data-api', '[data-toggle="suggestbtn"]', function (e) {
        var $this = $(this), opts = $this.data('bjui.suggest.options')

        if (!opts) {
            var data = $this.data(), options = data.options

            if (options) {
                if (typeof options === 'string') {
                    if (options.trim().startsWith('{'))
                        options = options.toObj()
                    else
                        options = options.toFunc()
                }
                if (typeof options === 'function') {
                    options = options.apply()
                }
                if (typeof options === 'object') {
                    delete data.options

                    $.extend(data, options)
                }
            }
            opts = data
        }

        delete opts['bjui.suggest.options']

        Plugin.call($this, opts)

        e.preventDefault()
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-tags.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-tags.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // TAGS CLASS DEFINITION
    // ======================
    var Tags = function (element, options) {
        this.$element = $(element)
        this.options = options
        this.tools = this.TOOLS()
        this.$box = $('<div></div>')
        this.timeout = null
        this.$tagsArr = {}
        this.tags = []

        if (options.istag) {
            this.$element.addClass('tag-input')
            this.$box.addClass('bjui-tags')
        } else {
            this.$box.addClass('bjui-autocomplete')
            if (!this.$element.attr('size'))
                this.$box.addClass('fluid')
        }

        this.$element.after(this.$box)
        this.$element.appendTo(this.$box)
    }

    Tags.DEFAULTS = {
        width: 300,
        url: '',
        global: false,
        type: 'GET',
        term: 'term',
        tagname: 'tag',     // Appended "<input type='hidden'>" name attribute
        max: 0,         // The maximum allowable number of tags(0=unlimited)
        clear: false,     // If not found, clear the input characters
        lightCls: 'tags-highlight',
        istag: true,      // If only need auto complete, set false
        data: {},
        container: '',
        keys: {
            value: 'value',
            label: 'label'
        }
    }

    Tags.EVENTS = {
        afterCreated: 'aftercreated.bjui.tags'
    }

    Tags.prototype.TOOLS = function () {
        var that = this, options = this.options
        var tools = {
            keyDown: function (e) {
                if (e.which == 13) {
                    return false
                }
            },
            keyUp: function (e) {
                switch (e.which) {
                    case BJUI.keyCode.BACKSPACE:
                        if (!$.trim(that.$element.val()).length) {
                            that.tools.removeMenu()
                            return false
                        }
                        break
                    case BJUI.keyCode.ESC:
                        that.tools.removeMenu()
                        break
                    case BJUI.keyCode.DOWN:
                        if (!that.$menu || !that.$menu.length) return

                        var $highlight = that.$menu.find('> .' + options.lightCls),
                            $first = that.$menu.find('> li:first-child')

                        if (!$highlight.length) {
                            $first.addClass(options.lightCls)
                        } else {
                            var $hight_next = $highlight.removeClass(options.lightCls).next()

                            if ($hight_next.length) {
                                $hight_next.addClass(options.lightCls)
                            } else {
                                $first.addClass(options.lightCls)
                            }
                        }
                        return false
                        break
                    case BJUI.keyCode.UP:
                        if (!that.$menu || !that.$menu.length) return
                        var $highlight = that.$menu.find('> .' + options.lightCls),
                            $last = that.$menu.find('> li:last-child')

                        if (!$highlight.length) {
                            $last.addClass(options.lightCls)
                        } else {
                            var $hight_prev = $highlight.removeClass(options.lightCls).prev()

                            if ($hight_prev.length) {
                                $hight_prev.addClass(options.lightCls)
                            } else {
                                $last.addClass(options.lightCls)
                            }
                        }
                        return false
                        break
                    case BJUI.keyCode.ENTER:
                        if (options.max > 0 && that.$tagsArr.length >= options.max) return false

                        var label = false, value = false, item = null
                        var $selectedItem = that.$menu && that.$menu.find('> .' + options.lightCls)

                        if ($selectedItem && $selectedItem.length) {
                            label = $selectedItem.text()
                            item = $selectedItem.data('item')
                            value = item[options.keys.value]
                        } else {
                            label = $.trim(that.$element.val())

                            if (!label.length) return false
                            if (options.clear) {
                                if ($.inArray(label, that.tags) == -1) {
                                    options.istag && (that.$element.val(''))
                                    return false
                                }
                            }
                            value = label
                        }
                        if (!label) return

                        /* Check the repeatability */
                        var isRepeat = false

                        that.$tagsArr.length && that.$tagsArr.each(function () {
                            if ($(this).val() == value) {
                                isRepeat = true
                                return
                            }
                        })

                        if (isRepeat) {
                            options.istag && (that.$element.val(''))
                            return false
                        }

                        that.tools.createTag(label, value)
                        that.tools.removeMenu()
                        options.istag && (that.$element.val(''))

                        //events
                        $.proxy(that.tools.onAfterCreated(item, value), that)

                        return false
                        break
                }
            },
            query: function () {
                if (that.timeout) clearTimeout(that.timeout)

                that.timeout = setTimeout(that.tools.doQuery, 300)
            },
            doQuery: function () {
                var options = that.options

                if (options.max > 0 && that.$tagsArr.length >= options.max) return

                var term = that.$element.val(), $menu = that.$menu, tags = [], $item = null
                var $parentBox = that.$element.closest('.navtab-panel').length ? $.CurrentNavtab : $.CurrentDialog
                var postData = {}

                if (that.$element.closest('.bjui-layout').length) $parentBox = that.$element.closest('.bjui-layout')
                if (!term.length) return

                that.$element.one('ajaxStart', function () {
                    $parentBox.trigger('bjui.ajaxStart')
                }).one('ajaxStop', function () {
                    $parentBox.trigger('bjui.ajaxStop')
                })

                postData[options.term] = term
                $.extend(postData, typeof options.data === 'object' && options.data)

                options.url = decodeURI(options.url).replacePlh(that.$element.closest('.unitBox'))

                if (!options.url.isFinishedTm()) {
                    BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                    BJUI.debug('BJUI.Tags: The query tags url is incorrect, url: ' + options.url)
                    return
                }

                options.url = encodeURI(options.url)

                $.ajax({
                    url: options.url,
                    global: options.global,
                    type: options.type,
                    data: postData,
                    dataType: 'json',
                    success: function (json) {
                        if (json.length != 0) {
                            if (!$menu || !$menu.length) $menu = $('<ul class="bjui-tags-menu"></ul>')

                            $menu.empty().hide();

                            if (options.container)
                                $menu.css('position', 'absolute').appendTo(options.container)
                            else
                                $menu.appendTo(that.$box)

                            for (var i = 0; i < json.length; i++) {
                                var obj = json[i]

                                if (typeof obj === 'string') {
                                    obj = {}
                                    obj[options.keys.label] = json[i]
                                    obj[options.keys.value] = json[i]
                                }

                                $item = $('<li class="tags-item">' + obj[options.keys.label] + '</li>').data('item', obj)
                                $item.appendTo($menu)

                                tags.push(obj[options.keys.label])
                            }

                            that.tags = tags

                            var h = $(window).height() - that.$element.offset().top - 50, top = 0, left = 0

                            if (options.container) {
                                top = that.$element.offset().top + that.$element.outerHeight()
                                left = that.$element.offset().left
                            } else {
                                top = that.$element.position().top + that.$element.outerHeight()
                                left = that.$element.position().left
                            }

                            $menu
                                .css({ 'top': top, 'left': left })
                                .fadeIn()
                                .find('> li')
                                .hover(function () {
                                    $(this).addClass(options.lightCls).siblings().removeClass(options.lightCls)
                                }, function () {
                                    $(this).removeClass(options.lightCls)
                                })
                                .click(function () {
                                    var label = $(this).text()
                                    var item = $(this).data('item')
                                    var value = item[options.keys.value]
                                    var isRepeat = false

                                    that.$box.find('input:hidden').each(function () {
                                        if ($(this).val() == value) {
                                            isRepeat = true
                                            return
                                        }
                                    })

                                    if (isRepeat) {
                                        options.istag && (that.$element.val(''))
                                        $menu.remove()
                                        return
                                    }

                                    $.proxy(that.tools.createTag(label, value), that)

                                    $menu.remove()
                                    options.istag && (that.$element.val(''))

                                    //events
                                    $.proxy(that.tools.onAfterCreated(item, value), that)
                                })

                            if ($menu.height() > h) {
                                $menu.height(h - 10).css('overflow-y', 'scroll')
                            }

                            that.$menu = $menu
                        }
                    }
                })
            },
            createTag: function (label, value) {
                if (!that.options.istag) {
                    that.$element.val(label)

                    return
                }
                var $btn = $('<span class="label label-tag" data-value="' + value + '" style="margin-left: 1px; margin-top: 1px;"><i class="fa fa-tag"></i> ' + label + '&nbsp;&nbsp;<a href="#" class="close">&times;</a></span>')

                $btn
                    .insertBefore(that.$element)
                    .find('a.close')
                    .click(function () {
                        var value = $btn.data('value')

                        that.$box.find('input:hidden').each(function () {
                            if ($(this).val() == value) {
                                $(this).remove()
                            }
                        })

                        $btn.remove()
                        that.$tagsArr = that.$box.find('input[name="' + that.options.tagname + '"]')
                    })

                var $hidden = $('<input type="hidden" name="' + that.options.tagname + '">').val(value)

                $hidden.appendTo(that.$box)
                that.$tagsArr = that.$box.find('input[name="' + that.options.tagname + '"]')
            },
            removeMenu: function () {
                if (that.$menu) that.$menu.remove()
            },
            onAfterCreated: function (item, value) {
                var alltags = []

                that.$tagsArr.length && that.$tagsArr.each(function () {
                    alltags.push($(this).val())
                })

                that.$element.trigger(Tags.EVENTS.afterCreated, { item: item, value: value, tags: alltags.join(',') })
            }
        }

        return tools
    }

    Tags.prototype.init = function () {
        var that = this
        var $element = this.$element
        var options = this.options

        if (!(options.url)) {
            BJUI.debug('BJUI.Tags: Do query tags, url is undefined!')
            return
        }
        if (isNaN(this.options.max)) {
            BJUI.debug('BJUI.Tags: Parameter \'max\' is non-numeric type!')
            return
        }

        that.$box.on('click', function () {
            $element.focus()
        })

        if (options.istag)
            that.$box.css('width', options.width)

        $element
            //.on('blur', $.proxy(this.tools.removeMenu, this))
            .on('keydown', $.proxy(this.tools.keyDown, this))
            .on('keyup', $.proxy(this.tools.keyUp, this))

        if (!$.support.leadingWhitespace) { // for ie8
            $element.on('propertychange', $.proxy(this.tools.query, this))
        } else {
            $element.on('input', $.proxy(this.tools.query, this))
        }

        $(document).on('click.bjui.tags', $.proxy(function (e) {
            if (!$(e.target).closest(this.$box).length) this.tools.removeMenu()
        }, this))
    }

    Tags.prototype.destroy = function () {
        if (this.$tags) {
            this.$element.upwrap()
            $tags.remove()
        }
    }

    // TAGS PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments,
            property = option

        return this.each(function () {
            var $this = $(this),
                options = {},
                data

            $.extend(true, options, Tags.DEFAULTS, typeof option === 'object' && option)

            data = new Tags(this, options)

            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.tags

    $.fn.tags = Plugin
    $.fn.tags.Constructor = Tags

    // TAGS NO CONFLICT
    // =================

    $.fn.tags.noConflict = function () {
        $.fn.tags = old
        return this
    }

    // TAGS DATA-API
    // ==============

    $(document).on(BJUI.eventType.initUI, function (e) {
        $(e.target).find('input[data-toggle="tags"]').each(function () {
            var $this = $(this), data = $this.data(), options = data.options

            if (options) {
                if (typeof options === 'string') {
                    if (options.trim().startsWith('{'))
                        options = options.toObj()
                    else
                        options = options.toFunc()
                }
                if (typeof options === 'function') {
                    options = options.apply()
                }
                if (typeof options === 'object') {
                    delete data.options

                    $.extend(data, options)
                }
            } else {
                if (data.keys)
                    data.keys = data.keys.toObj()
                if (data.data)
                    data.data = data.data.toObj()
            }

            Plugin.call($this, data)
        })
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-theme.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-theme.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // THEME GLOBAL ELEMENTS
    // ======================

    var $themeLink, $themeLis, $fontLis

    $(function () {
        var INIT_THEME = function () {
            $themeLink = $('#bjui-link-theme')
            $themeLis = $('#bjui-themes')
            $fontLis = $('#bjui-fonts')

            if ($.cookie) {
                var themeName = $.cookie('bjui_theme') || 'blue'
                var $li = $themeLis.find('a.theme_' + themeName)

                $li.theme({})

                /* font */
                var fontSize = $.cookie('bjui_font') || 'bjui-font-c'
                var $fontLi = $fontLis.find('a.' + fontSize)

                $fontLi.theme('setFont', fontSize)
            }
        }

        INIT_THEME()
    })

    // THEME CLASS DEFINITION
    // ======================
    var Theme = function (element, options) {
        this.$element = $(element)
        this.options = options
    }

    Theme.DEFAULTS = {
        theme: 'purple'
    }

    Theme.prototype.init = function () {
        if (!$themeLink.length) return
        var themeHref = $themeLink.attr('href')

        themeHref = themeHref.substring(0, themeHref.lastIndexOf('/'))
        themeHref = themeHref.substring(0, themeHref.lastIndexOf('/'))
        themeHref += '/' + this.options.theme + '/core.css'
        $themeLink.attr('href', themeHref)

        var $themeA = this.$element.closest('li').filter('.active')
        var classA = $themeA.data('theme') || 'default'

        classA = classA.replace(/(theme[\s][a-z]*)/g, '')
        $themeA.removeClass().addClass(classA).addClass('theme').addClass(this.options.theme)
        $themeLis.find('li').removeClass('active')
        this.$element.parent().addClass('active')
        this.cookie()
    }

    Theme.prototype.setTheme = function (themeName) {
        $themeLis.find('a.theme_' + themeName).trigger('click')
    }

    Theme.prototype.setFont = function (fontSize) {
        $('html').removeClass().addClass(fontSize)

        this.$element.closest('ul').prev().html(this.$element.html()).removeClass().addClass('dropdown-toggle bjui-fonts-tit ' + fontSize)

        $('body').find('table').each(function () {
            var $table = $(this), datagrid = $table.data('bjui.datagrid')

            if (datagrid) {
                datagrid.needfixedWidth = true
                $table.datagrid('fixedWidth')
                datagrid.needfixedWidth = false
            }
        })

        $(window).trigger(BJUI.eventType.resizeGrid)

        if ($.cookie) $.cookie('bjui_font', fontSize, { path: '/', expires: 30 });
    }

    Theme.prototype.cookie = function () {
        var theme = this.options.theme

        if ($.cookie) $.cookie('bjui_theme', theme, { path: '/', expires: 30 });
    }

    // THEME PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments
        var property = option

        return this.each(function () {
            var $this = $(this)
            var options = $.extend({}, Theme.DEFAULTS, $this.data(), typeof option === 'object' && option)
            var data = $this.data('bjui.theme')

            if (!data) $this.data('bjui.theme', (data = new Theme(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.theme

    $.fn.theme = Plugin
    $.fn.theme.Constructor = Theme

    // THEME NO CONFLICT
    // =================

    $.fn.theme.noConflict = function () {
        $.fn.theme = old
        return this
    }

    // THEME DATA-API
    // ==============

    $(document).on('click.bjui.theme.data-api', '[data-toggle="theme"]', function (e) {
        Plugin.call($(this))

        e.preventDefault()
    })

    $(document).on('click.bjui.fonts.data-api', '[data-toggle="fonts"]', function (e) {
        Plugin.call($(this), 'setFont', $(this).attr('class'))

        e.preventDefault()
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-initui.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-initui.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // INITUI CLASS DEFINITION
    // ======================
    var Initui = function (element, options) {
        var $this = this
        this.$element = $(element)
        this.options = options
    }

    Initui.DEFAULTS = {}

    Initui.prototype.init = function () {
        var that = this, $element = that.$element

        $.when(that.initUI()).done(function () {
            $element.trigger(BJUI.eventType.afterInitUI)
        })
    }

    Initui.prototype.initUI = function () {
        var $element = this.$element

        $.when($element.trigger(BJUI.eventType.beforeInitUI)).done(function () {
            $element.trigger(BJUI.eventType.initUI)
        })
    }

    // INITUI PLUGIN DEFINITION
    // =======================

    function Plugin(option) {
        var args = arguments,
            property = option

        return this.each(function () {
            var $this = $(this),
                options = $.extend({}, Initui.DEFAULTS, $this.data(), typeof option === 'object' && option),
                data = $this.data('bjui.initui')

            if (!data) $this.data('bjui.initui', (data = new Initui(this, options)))

            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.initui

    $.fn.initui = Plugin
    $.fn.initui.Constructor = Initui

    // INITUI NO CONFLICT
    // =================

    $.fn.initui.noConflict = function () {
        $.fn.initui = old
        return this
    }

    // INITUI DATA-API
    // ==============

    $(document).on('click.bjui.initui.data-api', '[data-toggle="initui"]', function (e) {
        Plugin.call($this, $this.data())

        e.preventDefault()
    })

    /* beforeInitUI */
    $(document).on(BJUI.eventType.beforeInitUI, function (e) {
        var $box = $(e.target),
            noinits = [],
            $noinit = $box.find('[data-noinit], pre')

        //progress
        $box.find('> .bjui-maskProgress').find('.progress').stop().animate({ width: '85%' }, 'fast')

        // Hide not need to initialize the UI DOM
        $noinit.each(function (i) {
            var $this = $(this), pos = {}

            pos.$target = $this
            pos.$next = $this.next()
            pos.$prev = $this.prev()
            pos.$parent = $this.parent()
            pos.visible = $this.is(':visible') ? true : false

            noinits.push(pos)
            $this.remove()
        })

        $box.data('bjui.noinit', noinits)
    })

    /* initUI */
    $(document).on(BJUI.eventType.initUI, function (e) {
        var $box = $(e.target)

        //progress
        $box.find('> .bjui-maskProgress').find('.progress').stop().animate({ width: '95%' }, 'fast')
    })

    /* afterInitUI */
    $(document).on(BJUI.eventType.afterInitUI, function (e) {
        var $box = $(e.target),
            noinits = $box.data('bjui.noinit'),
            $form = $box.find('> .bjui-pageContent').find('form')

        // Recovery not need to initialize the UI DOM
        if (noinits) {
            $.each(noinits, function (i, n) {
                if (n.$next.length) n.$next.before(n.$target)
                else if (n.$prev.length) n.$prev.after(n.$target)
                else if (n.$parent.length) n.$parent.append(n.$target)

                if (n.visible) n.$target.show()

                $box.removeData('bjui.noinit')
            })
        }

        /* resizePageH */
        $box.resizePageH()

        //submit
        if ($form.length) {
            $box.find('> .bjui-pageFooter').find(':submit').on('click.bjui.submit', function (e) {
                e.preventDefault()

                $form.submit()
            })
        }

        $box.find('> .bjui-pageFooter').find(':submit.btn-submit-header').on('click.bjui.submit', function (e) {
            e.preventDefault()

            var $form = $box.find('> .bjui-pageHeader').find('form')

            $form.length && $form.submit()
        })

        //progress
        $box.find('> .bjui-maskProgress').find('.progress').stop().animate({ width: '100%' }, 'fast', function () {
            $box.find('> .bjui-ajax-mask').fadeOut('normal', function () { $(this).remove() })
        })
    })

    /* Lateral Navigation */
    $(document).one(BJUI.eventType.afterInitUI, function (e) {
        var $hnavbar = $('#bjui-hnav-navbar'), $active = $hnavbar.find('> li.active'), $a = $active.find('> a')

        if ($active.length) {
            if ($active.find('> .items').length) {
                $a.trigger('click')
            } else {
                var href = $a.attr('href')

                if (href && !(href.startsWith('#') || href.startsWith('javascript'))) {
                    $a.trigger('click')
                }
            }
        }
    })

    /* ajaxStatus */
    var bjui_ajaxStatus = function ($target) {
        var $this = $target,
            $offset = $this,
            position = $this.css('position'),
            top = $this.scrollTop() || 0,
            height = ($this[0].clientHeight) / 2

        if (position == 'static') {
            $this.css('position', 'relative').data('bjui.ajax.static', true)
            $offset = $this.offsetParent()
        }

        var zIndex = parseInt($offset.css('zIndex')) || 0,
            $ajaxBackground = $this.find('> .bjui-maskBackground'),
            $ajaxProgress = $this.find('> .bjui-maskProgress')

        if (!$ajaxBackground.length) {
            $ajaxBackground = $(FRAG.maskBackground)
            $ajaxProgress = $(BJUI.doRegional(FRAG.maskProgress, BJUI.regional))
            $this.prepend($ajaxBackground).prepend($ajaxProgress)
        }

        var bgZindex = parseInt($ajaxBackground.css('zIndex')) || 0,
            prZindex = parseInt($ajaxProgress.css('zIndex')) || 0

        $ajaxBackground.css('zIndex', zIndex + 1).css('top', top)
        $ajaxProgress.css('zIndex', zIndex + 2)

        if (top)
            $ajaxProgress.css('top', top + height)

        if (height == 0) {
            setTimeout(function () {
                $ajaxProgress.css('top', $(this).scrollTop() + $this[0].clientHeight / 2)
            }, 50)
        }

        $this.off('scroll.ajaxmask').on('scroll.ajaxmask', function () {
            var top = $(this).scrollTop()

            $ajaxBackground.css('top', $this.scrollTop())
            $ajaxProgress.css('top', top + this.clientHeight / 2)
        })

        return { $bg: $ajaxBackground, $pr: $ajaxProgress }
    }

    $(document)
        .on('bjui.ajaxStart', function (e, timeout, callback) {
            var ajaxMask = bjui_ajaxStatus($(e.target))

            ajaxMask.$bg.fadeIn()
            ajaxMask.$pr.fadeIn()
            ajaxMask.$pr.find('.progress').animate({ width: '80%' }, timeout || 500)

            if (callback) {
                setTimeout(function () {
                    callback.toFunc().call(this)
                }, 25)
            }
        })
        .on('bjui.ajaxStop', function (e) {
            var $target = $(e.target), ajaxMask = bjui_ajaxStatus($target)

            ajaxMask.$pr.find('.progress').animate({ width: '100%' }, 'fast', function () {
                ajaxMask.$bg.remove()
                ajaxMask.$pr.remove()

                if ($target.data('bjui.ajax.static'))
                    $target.css('position', 'static')
            })
        })
        .on('bjui.ajaxError', function (e) {
            var $target = $(e.target), ajaxMask = bjui_ajaxStatus($target)

            ajaxMask.$bg.remove()
            ajaxMask.$pr.remove()

            if ($target.data('bjui.ajax.static'))
                $target.css('position', 'static')
        })

    $(document).on(BJUI.eventType.ajaxStatus, function (e) {
        var $target = $(e.target), ajaxMask = bjui_ajaxStatus($target)

        $target
            .one('ajaxStart', function () {
                ajaxMask.$bg.fadeIn()
                ajaxMask.$pr.fadeIn()

                ajaxMask.$pr.find('.progress').animate({ width: '10%' }, 'fast')
            })
            .one('ajaxStop', function () {
                //ajaxMask.$bg.fadeOut()
                //ajaxMask.$pr.fadeOut()
                //ajaxMask.$pr.find('.progress').animate({width:'80%'}, 'fast')
            })
            .one('ajaxError', function () {
                ajaxMask.$bg.remove()
                ajaxMask.$pr.remove()

                if ($target.data('bjui.ajax.static'))
                    $target.css('position', 'static')
            })
    })

    /* Clean plugins generated 'Dom elements' in the body */
    var bodyClear = function ($target) {
        $target.find('select[data-toggle="selectpicker"]').selectpicker('destroyMenu')
        $target.find('[data-toggle="selectztree"]').trigger('destroy.bjui.selectztree')
        $target.find('.bjui-suggest-input').trigger('destroy.bjui.suggest')
    }

    $(document).on(BJUI.eventType.beforeLoadDialog, function (e) {

    }).on(BJUI.eventType.beforeAjaxLoad, function (e) {
        bodyClear($(e.target))
    }).on(BJUI.eventType.beforeCloseNavtab, function (e) {
        bodyClear($(e.target))
    }).on(BJUI.eventType.beforeCloseDialog, function (e) {
        bodyClear($(e.target))
    })

    /* other */
    $(function () {
        $(document).on('keydown keyup', function (e) {
            if (e.which === BJUI.keyCode.CTRL) {
                BJUI.KeyPressed.ctrl = e.type == 'keydown' ? true : false
            }
            if (e.which === BJUI.keyCode.SHIFT) {
                BJUI.KeyPressed.shift = e.type == 'keydown' ? true : false
            }
        })
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-plugins.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-plugins.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    $(document).on(BJUI.eventType.initUI, function (e) {
        var $box = $(e.target)

        // UI init begin...

        /* i-check */
        if ($.fn.iCheck) {
            var $icheck = $box.find('input[data-toggle="icheck"]')

            $icheck.each(function (i) {
                var $element = $(this),
                    id = $element.attr('id'),
                    name = $element.attr('name'),
                    label = $element.data('label')

                if (label) $element.after('<label for="' + id + '" class="ilabel">' + label + '</label>')

                $element
                    .on('ifCreated', function (e) {
                        /* Fixed validate msgbox position */
                        var $parent = $(this).closest('div'),
                            $ilabel = $parent.next('[for="' + id + '"]')

                        $parent.attr('data-icheck', name)
                        $ilabel.attr('data-icheck', name)
                    })
                    .iCheck({
                        checkboxClass: 'icheckbox_minimal-purple',
                        radioClass: 'iradio_minimal-purple',
                        increaseArea: '20%' // optional
                    })
                    .on('ifChanged', function () {
                        /* Trigger validation */
                        $.fn.validator && $(this).trigger('validate')
                    })

                if ($element.prop('disabled')) $element.iCheck('disable')
            })
            /* i-check check all */
            $icheck.filter('.checkboxCtrl').on('ifChanged', function (e) {
                var checked = e.target.checked == true ? 'check' : 'uncheck'
                var group = $(this).data('group')

                $box.find(':checkbox[name="' + group + '"]').iCheck(checked)
            })
        }

        /* fixed ui style */
        $box.find('button').each(function () {
            var $element = $(this), icon = $element.data('icon')

            $element.addClass('btn')

            if (icon && !$element.find('> i').length) {
                icon = 'fa-' + icon.replace('fa-', '')

                if (!$element.data('bjui.icon')) {
                    $element.html('<i class="fa ' + icon + '"></i> ' + $element.html()).data('bjui.icon', true)
                }
            }
        })

        $box.find('input:text, input:password').each(function () {
            var $element = $(this).addClass('form-control'), size = $element.attr('size') || 0, width = size * 10

            width && $element.css('width', width)
        })

        $box.find('textarea').each(function () {
            var $element = $(this).addClass('form-control'), cols = $element.attr('cols') || 0, width = cols * 10, toggle = $element.attr('data-toggle')

            width && $element.css('width', width)

            if (toggle && toggle == 'autoheight' && $.fn.autosize)
                $element.addClass('autosize').autosize()
        })

        $box.find('a.btn').each(function () {
            var $element = $(this), icon = $element.data('icon')

            if (icon && !$element.find('> i').length) {
                icon = 'fa-' + icon.replace('fa-', '')

                if (!$element.data('bjui.icon')) {
                    $element.html('<i class="fa ' + icon + '"></i> ' + $element.html()).data('bjui.icon', true)
                }
            }
        })

        /* form validate */
        if ($.fn.validator) {
            $box.find('form[data-toggle="validate"]').each(function () {
                var $element = $(this)

                $(this)
                    .validator({
                        valid: function (form) {
                            $(form).bjuiajax('_ajaxform', $(form), $(form).data())
                        },
                        validClass: 'ok',
                        msgClass: 'n-bottom',
                        theme: 'red_bottom_effect_grid'
                    })
                    .on('invalid.form', function (e, form, errors) {
                        if ($(form).data('alertmsg')) $(form).alertmsg('error', BJUI.regional.validatemsg.replaceMsg(errors.length))
                    })
            })
        }

        /* moreSearch */
        $box.find('[data-toggle="moresearch"]').each(function () {
            var $element = $(this),
                $parent = $element.closest('.bjui-pageHeader'),
                $more = $parent && $parent.find('.bjui-moreSearch'),
                name = $element.data('name')

            if (!$element.attr('title')) $element.attr('title', transLang('更多查询条件'))
            $element.click(function (e) {
                if (!$more.length) {
                    BJUI.debug('Not created \'moresearch\' box[class="bjui-moreSearch"]!')
                    return
                }
                $more.css('top', $parent.outerHeight() - 1)
                if ($more.is(':visible')) {
                    $element.html('<i class="fa fa-angle-double-down"></i>')
                    if (name) $('body').data('moresearch.' + name, false)
                } else {
                    $element.html('<i class="fa fa-angle-double-up"></i>')
                    if (name) $('body').data('moresearch.' + name, true)
                }
                $more.fadeToggle('slow', 'linear')

                e.preventDefault()
            })

            if (name && $('body').data('moresearch.' + name)) {
                $more.css('top', $parent.outerHeight() - 1).fadeIn()
                $element.html('<i class="fa fa-angle-double-up"></i>')
            }
        })

        /* bootstrap - select */
        if ($.fn.selectpicker) {
            var $selectpicker = $box.find('select[data-toggle="selectpicker"]')
            var bjui_select_linkage = function ($obj, $next) {
                if (!$next || !$next.length) return

                var refurl = $obj.data('refurl')
                var _setEmpty = function ($select) {
                    var $_nextselect = $($select.data('nextselect'))

                    if ($_nextselect && $_nextselect.length) {
                        var emptytxt = $_nextselect.data('emptytxt') || '&nbsp;'

                        $_nextselect.html('<option>' + emptytxt + '</option>').selectpicker('refresh')
                        _setEmpty($_nextselect)
                    }
                }

                if (($next && $next.length) && refurl) {
                    var val = $obj.data('val'), nextVal = $next.data('val'), keys = $obj.data('keys')

                    if (keys && typeof keys === 'string')
                        keys = keys.toObj()
                    if (!keys)
                        keys = {}

                    if (typeof val === 'undefined') val = $obj.val()
                    $.ajax({
                        type: 'POST',
                        dataType: 'json',
                        url: refurl.replace('{value}', encodeURIComponent(val)),
                        cache: false,
                        data: {},
                        success: function (json) {
                            if (!json) return

                            var html = '', selected = ''

                            $.each(json, function (i) {
                                var value, label

                                if (json[i] && json[i].length) {
                                    value = json[i][0]
                                    label = json[i][1]
                                } else {
                                    value = json[i][keys.value || 'value']
                                    label = json[i][keys.label || 'label']
                                }
                                if (typeof nextVal !== 'undefined') selected = value == nextVal ? ' selected' : ''
                                html += '<option value="' + value + '"' + selected + '>' + label + '</option>'
                            })

                            $obj.removeAttr('data-val').removeData('val')
                            $next.removeAttr('data-val').removeData('val')

                            if (!html) {
                                html = $next.data('emptytxt') || '&nbsp;'
                                html = '<option>' + html + '</option>'
                            }

                            $next.html(html).selectpicker('refresh')
                            _setEmpty($next)
                        },
                        error: BJUI.ajaxError
                    })
                }
            }

            $selectpicker.each(function () {
                var $element = $(this)
                var options = $element.data()
                var $next = $(options.nextselect)

                $element.addClass('show-tick')
                if (!options.style) $element.data('style', 'btn-default')
                if (!options.width) $element.data('width', 'auto')
                if (!options.container) $element.data('container', 'body')
                else if (options.container == true) $element.attr('data-container', 'false').data('container', false)

                $element.selectpicker()

                if ($next && $next.length && (typeof $next.data('val') != 'undefined'))
                    bjui_select_linkage($element, $next)
            })

            /* bootstrap - select - linkage && Trigger validation */
            $selectpicker.change(function () {
                var $element = $(this)
                var $nextselect = $($element.data('nextselect'))

                bjui_select_linkage($element, $nextselect)

                /* Trigger validation */
                if ($element.attr('aria-required')) {
                    $.fn.validator && $element.trigger('validate')
                }
            })
        }

        if ($.fn.zTree) {
            /* zTree - plugin */
            $box.find('[data-toggle="ztree"]').each(function () {
                var $this = $(this), op = $this.data(), options = op.options, _setting

                if (options && typeof options === 'string') options = options.toObj()
                if (options) $.extend(op, typeof options === 'object' && options)

                _setting = op.setting

                if (!op.nodes) {
                    op.nodes = []
                    $this.find('> li').each(function () {
                        var $li = $(this)
                        var node = $li.data()

                        if (node.pid) node.pId = node.pid
                        node.name = $li.html()
                        op.nodes.push(node)
                    })
                    $this.empty()
                } else {
                    if (typeof op.nodes === 'string') {
                        if (op.nodes.trim().startsWith('[') || op.nodes.trim().startsWith('{')) {
                            op.nodes = op.nodes.toObj()
                        } else {
                            op.nodes = op.nodes.toFunc()
                        }
                    }
                    if (typeof op.nodes === 'function') {
                        op.nodes = op.nodes.call(this)
                    }

                    $this.removeAttr('data-nodes')
                }

                if (!op.showRemoveBtn) op.showRemoveBtn = false
                if (!op.showRenameBtn) op.showRenameBtn = false
                if (op.addHoverDom && typeof op.addHoverDom !== 'function') op.addHoverDom = (op.addHoverDom === 'edit') ? _addHoverDom : op.addHoverDom.toFunc()
                if (op.removeHoverDom && typeof op.removeHoverDom !== 'function') op.removeHoverDom = (op.removeHoverDom === 'edit') ? _removeHoverDom : op.removeHoverDom.toFunc()
                if (!op.maxAddLevel) op.maxAddLevel = 2

                var setting = {
                    view: {
                        addHoverDom: op.addHoverDom || null,
                        removeHoverDom: op.removeHoverDom || null,
                        addDiyDom: op.addDiyDom ? op.addDiyDom.toFunc() : null
                    },
                    edit: {
                        enable: op.editEnable,
                        showRemoveBtn: op.showRemoveBtn,
                        showRenameBtn: op.showRenameBtn
                    },
                    check: {
                        enable: op.checkEnable,
                        chkStyle: op.chkStyle,
                        radioType: op.radioType
                    },
                    callback: {
                        onClick: op.onClick ? op.onClick.toFunc() : null,
                        beforeDrag: op.beforeDrag ? op.beforeDrag.toFunc() : _beforeDrag,
                        beforeDrop: op.beforeDrop ? op.beforeDrop.toFunc() : _beforeDrop,
                        onDrop: op.onDrop ? op.onDrop.toFunc() : null,
                        onCheck: op.onCheck ? op.onCheck.toFunc() : null,
                        beforeRemove: op.beforeRemove ? op.beforeRemove.toFunc() : null,
                        onRemove: op.onRemove ? op.onRemove.toFunc() : null,
                        onNodeCreated: _onNodeCreated,
                        onCollapse: _onCollapse,
                        onExpand: _onExpand
                    },
                    data: {
                        simpleData: {
                            enable: op.simpleData || true
                        },
                        key: {
                            title: op.title || ''
                        }
                    }
                }

                if (_setting && typeof _setting === 'string') _setting = _setting.toObj()
                if (_setting) $.extend(true, setting, typeof _setting === 'object' && _setting)

                $.fn.zTree.init($this, setting, op.nodes)

                var IDMark_A = '_a'
                var zTree = $.fn.zTree.getZTreeObj($this.attr('id'))

                if (op.expandAll) zTree.expandAll(true)

                // onCreated
                function _onNodeCreated(event, treeId, treeNode) {
                    if (treeNode.faicon) {
                        var $a = $('#' + treeNode.tId + '_a')

                        if (!$a.data('faicon')) {
                            $a.data('faicon', true)
                                .addClass('faicon')
                                .find('> span.button').append('<i class="fa fa-' + treeNode.faicon + '"></i>')
                        }
                    }
                    if (op.onNodeCreated) {
                        op.onNodeCreated.toFunc().call(this, event, treeId, treeNode)
                    }
                }
                // onCollapse
                function _onCollapse(event, treeId, treeNode) {
                    if (treeNode.faiconClose) {
                        $('#' + treeNode.tId + '_ico').find('> i').attr('class', 'fa fa-' + treeNode.faiconClose)
                    }

                    if (op.onCollapse) {
                        op.onCollapse.toFunc().call(this, event, treeId, treeNode)
                    }
                }
                // onExpand
                function _onExpand(event, treeId, treeNode) {
                    if (treeNode.faicon && treeNode.faiconClose) {
                        $('#' + treeNode.tId + '_ico').find('> i').attr('class', 'fa fa-' + treeNode.faicon)
                    }
                    if (op.onExpand) {
                        op.onExpand.toFunc().call(this, event, treeId, treeNode)
                    }
                }
                // add button, del button
                function _addHoverDom(treeId, treeNode) {
                    var level = treeNode.level
                    var $obj = $('#' + treeNode.tId + IDMark_A)
                    var $add = $('#diyBtn_add_' + treeNode.id)
                    var $del = $('#diyBtn_del_' + treeNode.id)

                    if (!$add.length) {
                        if (level < op.maxAddLevel) {
                            $add = $('<span class="tree_add" id="diyBtn_add_' + treeNode.id + '" title="' + transLang('添加') + '"></span>')
                            $add.appendTo($obj);
                            $add.on('click', function () {
                                zTree.addNodes(treeNode, { name: '' + transLang('新增') + 'Item' })
                            })
                        }
                    }

                    if (!$del.length) {
                        var $del = $('<span class="tree_del" id="diyBtn_del_' + treeNode.id + '" title="' + transLang('删除') + '"></span>')

                        $del
                            .appendTo($obj)
                            .on('click', function (event) {
                                var delFn = function () {
                                    $del.alertmsg('confirm', transLang('确认要删除 ') + treeNode.name + '？', {
                                        okCall: function () {
                                            zTree.removeNode(treeNode)
                                            if (op.onRemove) {
                                                var fn = op.onRemove.toFunc()

                                                if (fn) fn.call(this, event, treeId, treeNode)
                                            }
                                        },
                                        cancelCall: function () {
                                            return
                                        }
                                    })
                                }

                                if (op.beforeRemove) {
                                    var fn = op.beforeRemove.toFunc()

                                    if (fn) {
                                        var isdel = fn.call(fn, treeId, treeNode)

                                        if (isdel && isdel == true) delFn()
                                    }
                                } else {
                                    delFn()
                                }
                            }
                            )
                    }
                }

                // remove add button && del button
                function _removeHoverDom(treeId, treeNode) {
                    var $add = $('#diyBtn_add_' + treeNode.id)
                    var $del = $('#diyBtn_del_' + treeNode.id)

                    if ($add && $add.length) {
                        $add.off('click').remove()
                    }

                    if ($del && $del.length) {
                        $del.off('click').remove()
                    }
                }

                // Drag
                function _beforeDrag(treeId, treeNodes) {
                    for (var i = 0; i < treeNodes.length; i++) {
                        if (treeNodes[i].drag === false) {
                            return false
                        }
                    }
                    return true
                }

                function _beforeDrop(treeId, treeNodes, targetNode, moveType) {
                    return targetNode ? targetNode.drop !== false : true
                }
            })

            /* zTree - drop-down selector */

            var $selectzTree = $box.find('[data-toggle="selectztree"]')

            $selectzTree.each(function () {
                var $this = $(this)
                var options = $this.data(),
                    $tree = $(options.tree),
                    w = parseFloat($this.css('width')),
                    h = $this.outerHeight()

                options.width = options.width || $this.outerWidth()
                options.height = options.height || 'auto'

                if (!$tree || !$tree.length) return

                var treeid = $tree.attr('id')
                var $box = $('#' + treeid + '_select_box')
                var setPosition = function ($box) {
                    var top = $this.offset().top,
                        left = $this.offset().left,
                        $clone = $tree.clone().appendTo($('body')),
                        treeHeight = $clone.outerHeight()

                    $clone.remove()

                    var offsetBot = $(window).height() - treeHeight - top - h,
                        maxHeight = $(window).height() - top - h

                    if (options.height == 'auto' && offsetBot < 0) maxHeight = maxHeight + offsetBot
                    $box.css({ top: (top + h), left: left, 'max-height': maxHeight })
                }

                $this.click(function () {
                    if ($box && $box.length) {
                        setPosition($box)
                        $box.show()
                        return
                    }

                    var zindex = 2
                    var dialog = $.CurrentDialog

                    if (dialog && dialog.length) {
                        zindex = dialog.css('zIndex') + 1
                    }
                    $box = $('<div id="' + treeid + '_select_box" class="tree-box"></div>')
                        .css({ position: 'absolute', 'zIndex': zindex, 'min-width': options.width, height: options.height, overflow: 'auto', background: '#FAFAFA', border: '1px #EEE solid' })
                        .hide()
                        .appendTo($('body'))

                    $tree.appendTo($box).css('width', '100%').data('fromObj', $this).removeClass('hide').show()
                    setPosition($box)
                    $box.show()
                })

                $('body').on('mousedown', function (e) {
                    var $target = $(e.target)

                    if (!($this[0] == e.target || ($box && $box.length > 0 && $target.closest('.tree-box').length > 0))) {
                        $box.hide()
                    }
                })

                var $scroll = $this.closest('.bjui-pageContent')

                if ($scroll && $scroll.length) {
                    $scroll.scroll(function () {
                        if ($box && $box.length) {
                            setPosition($box)
                        }
                    })
                }

                //destroy selectzTree
                $this.on('destroy.bjui.selectztree', function () {
                    $box.remove()
                })
            })
        }

        /* Kindeditor */
        if (KindEditor) {
            $box.find('textarea[data-toggle="kindeditor"]').each(function () {
                var $editor = $(this).removeClass('form-control'), options = $editor.data(), _op = options.options

                if (_op && typeof _op === 'string') _op = _op.toObj()
                if (_op && typeof _op === 'object') {
                    $.extend(options, _op)
                    delete options.options
                }

                if (options.items && typeof options.items === 'string')
                    options.items = options.items.replaceAll('\'', '').replaceAll(' ', '').split(',')
                if (options.afterUpload) options.afterUpload = options.afterUpload.toFunc()
                if (options.afterSelectFile) options.afterSelectFile = options.afterSelectFile.toFunc()
                if (options.confirmSelect) options.confirmSelect = options.confirmSelect.toFunc()

                var htmlTags = {
                    font: [/*'color', 'size', 'face', '.background-color'*/],
                    span: ['.color', '.background-color', '.font-size', '.font-family'
                        /*'.color', '.background-color', '.font-size', '.font-family', '.background',
                        '.font-weight', '.font-style', '.text-decoration', '.vertical-align', '.line-height'*/
                    ],
                    div: ['.margin', '.padding', '.text-align'
                        /*'align', '.border', '.margin', '.padding', '.text-align', '.color',
                        '.background-color', '.font-size', '.font-family', '.font-weight', '.background',
                        '.font-style', '.text-decoration', '.vertical-align', '.margin-left'*/
                    ],
                    table: ['align', 'width'
                        /*'border', 'cellspacing', 'cellpadding', 'width', 'height', 'align', 'bordercolor',
                        '.padding', '.margin', '.border', 'bgcolor', '.text-align', '.color', '.background-color',
                        '.font-size', '.font-family', '.font-weight', '.font-style', '.text-decoration', '.background',
                        '.width', '.height', '.border-collapse'*/
                    ],
                    'td,th': ['align', 'valign', 'width', 'height', 'colspan', 'rowspan'
                        /*'align', 'valign', 'width', 'height', 'colspan', 'rowspan', 'bgcolor',
                        '.text-align', '.color', '.background-color', '.font-size', '.font-family', '.font-weight',
                        '.font-style', '.text-decoration', '.vertical-align', '.background', '.border'*/
                    ],
                    a: ['href', 'target', 'name'],
                    embed: ['src', 'width', 'height', 'type', 'loop', 'autostart', 'quality', '.width', '.height', 'align', 'allowscriptaccess'],
                    img: ['src', 'width', 'height', 'border', 'alt', 'title', 'align', '.width', '.height', '.border'],
                    'p,ol,ul,li,blockquote,h1,h2,h3,h4,h5,h6': [
                        'class', 'align', '.text-align', '.color', /*'.background-color', '.font-size', '.font-family', '.background',*/
                        '.font-weight', '.font-style', '.text-decoration', '.vertical-align', '.text-indent', '.margin-left'
                    ],
                    pre: ['class'],
                    hr: ['class', '.page-break-after'],
                    'br,tbody,tr,strong,b,sub,sup,em,i,u,strike,s,del': []
                }

                KindEditor.create($editor, {
                    pasteType: options.pasteType,
                    minHeight: options.minHeight || 260,
                    autoHeightMode: options.autoHeight || false,
                    items: options.items || KindEditor.options.items,
                    uploadJson: options.uploadJson,
                    fileManagerJson: options.fileManagerJson,
                    allowFileManager: options.allowFileManager || true,
                    //fillDescAfterUploadImage : options.fillDescAfterUploadImage || true, //上传图片成功后转到属性页，为false则直接插入图片[设为true方便自定义函数(X_afterSelect)]
                    afterUpload: options.afterUpload,
                    afterSelectFile: options.afterSelectFile,
                    //X_afterSelect            : options.confirmSelect,
                    //htmlTags                 : htmlTags,
                    /*
                    cssPath                  : [
                                                    BJUI.PLUGINPATH + 'kindeditor_4.1.10/editor-content.css', 
                                                    BJUI.PLUGINPATH + 'kindeditor_4.1.10/plugins/code/prettify.css'
                                               ],
                    */
                    afterBlur: function () { this.sync() }
                })
            })
        }

        if ($.fn.colorpicker) {
            /* colorpicker */
            $box.find('[data-toggle="colorpicker"]').each(function () {
                var $this = $(this)
                var isbgcolor = $this.data('bgcolor')

                $this.colorpicker()
                if (isbgcolor) {
                    $this.on('changeColor', function (ev) {
                        $this.css('background-color', ev.color.toHex())
                    })
                }
            })

            $box.find('[data-toggle="clearcolor"]').each(function () {
                var $this = $(this)
                var $target = $this.data('target') ? $($this.data('target')) : null

                if ($target && $target.length) {
                    $this.click(function () {
                        $target.val('')
                        if ($target.data('bgcolor')) $target.css('background-color', '')
                    })
                }
            })
        }

        /* tooltip */
        $box.find('[data-toggle="tooltip"]').each(function () {
            $(this).tooltip()
        })

        $box.find('[data-toggle="popover"]').each(function () {
            var $element = $(this), target = $element.data('target')

            if (target && $(target).length) {
                $element.attr('data-content', $(target).html())
            }

            $element.popover()
        })

        /* WebUploader */
        if (WebUploader) {
            var initWebUploader = function ($element, index) {
                var old = $element.data('webuploader'), options = $element.data('options')

                if (old) {
                    old.destroy()
                    $element.data('webuploader.wrap').remove()
                }

                if (options) {
                    if (typeof options === 'string') {
                        options = options.trim().toObj()
                    }

                    if (typeof options === 'object') {
                        $element.hide()

                        var $wrap = $('<div id="uploader" class="wu-example"><div class="queueList"><div id="dndArea" class="placeholder"><div id="filePicker"></div><p>或将文件拖到这里</p></div></div><div class="statusBar" style="display:none;"><div class="progress"><span class="text">0%</span><span class="percentage"></span></div><div class="info"></div><div class="btns"><div id="filePicker2"></div><div class="uploadBtn">开始上传</div></div></div>'),
                            // 图片容器
                            $queue = $('<ul class="filelist"></ul>').appendTo($wrap.find('.queueList')),
                            // 状态栏，包括进度和控制按钮
                            $statusBar = $wrap.find('.statusBar'),
                            // 文件总体选择信息。
                            $info = $statusBar.find('.info'),
                            // 上传按钮
                            $upload = $wrap.find('.uploadBtn'),
                            // 没选择文件之前的内容。
                            $placeHolder = $wrap.find('.placeholder'),
                            // 总体进度条
                            $progress = $statusBar.find('.progress').hide(),
                            // 添加的文件数量
                            fileCount = 0,
                            // 添加的文件总大小
                            fileSize = 0,
                            // 优化retina, 在retina下这个值是2
                            ratio = window.devicePixelRatio || 1,
                            // 缩略图大小
                            thumbnailWidth = 110 * ratio,
                            thumbnailHeight = 110 * ratio,
                            // 可能有pedding, ready, uploading, confirm, done.
                            state = 'pedding',
                            // 所有文件的进度信息，key为file id
                            percentages = {},
                            supportTransition = (function () {
                                var s = document.createElement('p').style,
                                    r = 'transition' in s ||
                                        'WebkitTransition' in s ||
                                        'MozTransition' in s ||
                                        'msTransition' in s ||
                                        'OTransition' in s

                                s = null

                                return r
                            })(),
                            // 图片访问基地址
                            basePath = options.basePath || '',
                            // WebUploader实例
                            uploader,
                            // 上传文件的单位(单位 + 类型)
                            upunit = options.upunit || transLang('张图片');

                        // 当有文件添加进来时执行，负责view的创建
                        var addFile = function (file, isuploaded, _index) {
                            if (!file && options.uploaded) {
                                $.each(options.uploaded.split(','), function (i, n) {
                                    var uploadedFile = { id: 'WU_FILE_UP_' + i, name: n.trim(), src: basePath + n.trim() }

                                    addFile(uploadedFile, true, i)

                                    fileCount++
                                })

                                if (fileCount) {
                                    $placeHolder.addClass('element-invisible');
                                    $statusBar.show()
                                    setState('uploaded')
                                }

                                return
                            }

                            if (fileCount >= options.fileNumLimit) {
                                $statusBar.find('#filePicker2').hide()
                            }

                            var uploadedAttr = isuploaded && upunit === transLang('张图片') ? ' style="cursor:pointer;" data-toggle="dialog" data-options="{id:\'bjui-dialog-view-upload-image\', image:\'' + encodeURIComponent(file.src) + '\', width:800, height:500, mask:true, title:\'' + transLang('查看已上传图片') + '\'}"' : '',
                                $li = $('<li class="' + (isuploaded ? 'uploaded' : '') + '" id="' + file.id + '_' + index + '">' +
                                    '<p class="title">' + file.name + '</p>' +
                                    '<p class="imgWrap" ' + uploadedAttr + '></p>' +
                                    '<p class="progress"><span></span></p>' +
                                    '</li>'),
                                $btns = $('<div class="file-panel">' +
                                    '<span class="cancel">' + transLang('删除') + '</span>' +
                                    '<span class="rotateRight">' + transLang('向右旋转') + '</span>' +
                                    '<span class="rotateLeft">' + transLang('向左旋转') + '</span></div>').appendTo($li),
                                $prgress = $li.find('p.progress span'),
                                $imgWrap = $li.find('p.imgWrap'),
                                $info = $('<p class="error"></p>'),
                                text = '',
                                showError = function (code) {
                                    switch (code) {
                                        case 'exceed_size':
                                            text = transLang('文件大小超出')

                                            break
                                        case 'interrupt':
                                            text = transLang('上传暂停')

                                            break
                                        default:
                                            text = transLang('上传失败，请重试')

                                            break
                                    }

                                    $info.text(text).appendTo($li)
                                }

                            if (!isuploaded) {
                                if (file.getStatus() === 'invalid') {
                                    showError(file.statusText)
                                } else {
                                    // @todo lazyload
                                    $imgWrap.text(transLang('预览中'))
                                    uploader.makeThumb(file, function (error, src) {
                                        if (error) {
                                            $imgWrap.text(transLang('不能预览'))
                                            return
                                        }

                                        var img = $('<img src="' + src + '">')

                                        $imgWrap.empty().append(img)
                                    }, thumbnailWidth, thumbnailHeight)

                                    percentages[file.id] = [file.size, 0]
                                    file.rotation = 0
                                }

                                file.on('statuschange', function (cur, prev) {
                                    if (prev === 'progress') {
                                        $prgress.hide().width(0)
                                    } else if (prev === 'queued') {

                                    }

                                    // 成功
                                    if (cur === 'error' || cur === 'invalid') {
                                        showError(file.statusText)
                                        percentages[file.id][1] = 1
                                    } else if (cur === 'interrupt') {
                                        showError('interrupt')
                                    } else if (cur === 'queued') {
                                        percentages[file.id][1] = 0
                                    } else if (cur === 'progress') {
                                        $info.remove()
                                        $prgress.css('display', 'block')
                                    } else if (cur === 'complete') {
                                        $li.append('<span class="success"></span>')
                                    }

                                    $li.removeClass('state-' + prev).addClass('state-' + cur)
                                })
                            } else {
                                $imgWrap.empty().append('<img src="' + file.src + '">')
                                if (options.initUploaded) {
                                    var arr = options.initUploaded.split(',')

                                    $li.append('<input type="hidden" class="upload" name="' + (options.upname || $element.data('name')) + '" value="' + arr[_index] + '">')
                                }
                            }

                            $li.on('mouseenter', function () {
                                $btns.stop().animate({ height: 30 })
                            })

                            $li.on('mouseleave', function () {
                                $btns.stop().animate({ height: 0 })
                            })

                            $btns.on('click', 'span', function () {
                                var index = $(this).index(),
                                    deg

                                switch (index) {
                                    case 0:
                                        if (isuploaded) {
                                            fileCount--
                                            removeFile(file)

                                            if (!fileCount) {
                                                setState('pedding');
                                            }
                                            uploader.refresh()
                                            updateTotalProgress()
                                        } else {
                                            uploader.removeFile(file)
                                        }

                                        return
                                    case 1:
                                        file.rotation += 90

                                        break
                                    case 2:
                                        file.rotation -= 90

                                        break
                                }

                                if (supportTransition) {
                                    deg = 'rotate(' + file.rotation + 'deg)'
                                    $imgWrap.css({
                                        '-webkit-transform': deg,
                                        '-mos-transform': deg,
                                        '-o-transform': deg,
                                        'transform': deg
                                    })
                                } else {
                                    $imgWrap.css('filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation=' + (~~((file.rotation / 90) % 4 + 4) % 4) + ')')
                                }
                            })

                            $li.appendTo($queue)
                        }

                        // 负责view的销毁
                        var removeFile = function (file) {
                            var $li = $wrap.find('#' + file.id + '_' + index)

                            delete percentages[file.id]
                            updateTotalProgress()

                            $li.off().find('.file-panel').off().end().remove()

                            if (fileCount < options.fileNumLimit) {
                                $statusBar.find('#filePicker2').show()
                            }
                        }

                        var updateTotalProgress = function () {
                            var loaded = 0,
                                total = 0,
                                spans = $progress.children(),
                                percent

                            $.each(percentages, function (k, v) {
                                total += v[0]
                                loaded += v[0] * v[1]
                            })

                            percent = total ? loaded / total : 0

                            spans.eq(0).text(Math.round(percent * 100) + '%')
                            spans.eq(1).css('width', Math.round(percent * 100) + '%')
                            updateStatus()
                        }

                        var updateStatus = function () {
                            var text = '', stats;

                            if (state === 'ready') {
                                text = transLang('选中') + fileCount + upunit + ',' + transLang('共') + WebUploader.formatSize(fileSize) + '.'
                            } else if (state === 'confirm') {
                                stats = uploader.getStats()
                                if (stats.uploadFailNum) {
                                    text = transLang('已成功上传') + stats.successNum + upunit + '，' +
                                        stats.uploadFailNum + upunit + transLang('上传失败') + '<a class="retry" href="#">' + transLang('重新上传') + '</a> ' + transLang('或') + ' <a class="ignore" href="#">' + transLang('忽略') + '</a>'
                                }

                            } else if (state === 'uploaded') {
                                text = transLang('已上传') + fileCount + upunit
                            } else {
                                stats = uploader.getStats()
                                text = transLang('共') + fileCount + upunit + '（' + WebUploader.formatSize(fileSize) + '），' + transLang('已上传') + stats.successNum

                                if (stats.uploadFailNum) {
                                    text += '，' + transLang('失败') + stats.uploadFailNum
                                }
                            }

                            $info.html(text)
                            $element.data('fileCount', fileCount)
                        }

                        var setState = function (val) {
                            var file, stats

                            if (val === state) {
                                return
                            }

                            $upload.removeClass('state-' + state)
                            $upload.addClass('state-' + val)
                            state = val

                            switch (state) {
                                case 'pedding':
                                    $placeHolder.removeClass('element-invisible')
                                    $queue.parent().removeClass('filled')
                                    $queue.hide();
                                    $statusBar.addClass('element-invisible')
                                    uploader.refresh()

                                    break
                                case 'ready':
                                    $placeHolder.addClass('element-invisible')
                                    $wrap.find('#filePicker2').removeClass('element-invisible')
                                    $queue.parent().addClass('filled')
                                    $queue.show()
                                    $statusBar.removeClass('element-invisible')
                                    uploader.refresh()
                                    $upload.removeClass('disabled')

                                    break
                                case 'uploading':
                                    $wrap.find('#filePicker2').addClass('element-invisible')
                                    $progress.show()
                                    $upload.text(transLang('暂停上传'))

                                    break
                                case 'paused':
                                    $progress.show()
                                    $upload.text(transLang('继续上传'))

                                    break
                                case 'confirm':
                                    $progress.hide()
                                    $upload.text(transLang('开始上传')).addClass('disabled')

                                    stats = uploader.getStats()
                                    if (stats.successNum && !stats.uploadFailNum) {
                                        setState('finish')
                                        return
                                    }

                                    break
                                case 'finish':
                                    stats = uploader.getStats()
                                    if (stats.successNum) {

                                    } else {
                                        // 没有成功的图片，重设
                                        state = 'done'

                                        BJUI.alertmsg('info', transLang('上传失败！'))
                                    }

                                    break
                                case 'uploaded':
                                    $upload.text(transLang('开始上传')).addClass('disabled')

                                    break
                            }

                            if (state !== 'uploaded')
                                updateStatus()
                        }

                        $wrap.insertAfter($element)

                        if (!WebUploader.Uploader.support()) {
                            alert('Web Uploader ' + transLang('不支持您的浏览器！如果你使用的是IE浏览器，请尝试升级 flash 播放器'));
                            throw new Error('WebUploader does not support the browser you are using.');
                        }

                        // 是否允许重新上传
                        if (typeof options.reupload === 'undefined')
                            options.reupload = true

                        options = $.extend(true, {}, {
                            pick: {
                                id: $wrap.find('#filePicker'),
                                label: transLang('点击选择图片')
                            },
                            dnd: $wrap.find('.queueList'),
                            paste: false,
                            accept: {
                                title: 'Images',
                                extensions: 'gif,jpg,jpeg,bmp,png',
                                mimeTypes: 'image/*'
                            },
                            swf: BJUI.PLUGINPATH + 'webuploader/Uploader.swf', // swf文件路径
                            disableGlobalDnd: false,
                            chunked: false,
                            server: null,
                            fileNumLimit: 300,
                            fileSizeLimit: 200 * 1024 * 1024,        // 200 M
                            fileSingleSizeLimit: 50 * 1024 * 1024    // 50 M
                        }, options)

                        // 实例化
                        uploader = WebUploader.create(options)

                        // 如果有已上传的图片(编辑时)
                        if (typeof $element.data('uploaded') !== 'undefined')
                            options.uploaded = $element.data('uploaded')
                        if (options.uploaded) {
                            // 将已上传图片加到队列
                            addFile()

                            $queue.parent().addClass('filled')
                        }

                        // 上传成功
                        uploader.on('uploadSuccess', function (file, response) {
                            if (response[BJUI.keys.statusCode] != BJUI.statusCode.ok) {
                                BJUI.alertmsg('error', response.message)
                            } else {
                                var $li = $wrap.find('#' + file.id + '_' + index)

                                $li.find('input.upload').remove().end()
                                    .append('<input type="hidden" class="upload" name="' + (options.upname || $element.data('name')) + '" value="' + response.filename + '">')
                            }
                        })
                        // 上传失败
                        uploader.on('uploadError', function (file, response) {
                            BJUI.alertmsg('error', response.message)
                        })

                        // 添加“添加文件”的按钮，
                        if (options.fileNumLimit > 1) {
                            uploader.addButton({
                                id: $wrap.find('#filePicker2'),
                                label: transLang('继续添加')
                            })

                            if (fileCount >= options.fileNumLimit)
                                $statusBar.find('#filePicker2').hide()
                        }

                        uploader.onUploadProgress = function (file, percentage) {
                            var $li = $wrap.find('#' + file.id + '_' + index),
                                $percent = $li.find('.progress span')

                            $percent.css('width', percentage * 100 + '%')
                            percentages[file.id][1] = percentage
                            updateTotalProgress()
                        }

                        uploader.onFileQueued = function (file) {
                            fileCount++
                            fileSize += file.size

                            if (fileCount === 1) {
                                $placeHolder.addClass('element-invisible');
                                $statusBar.show()
                            }

                            addFile(file)
                            setState('ready')
                            updateTotalProgress()
                        }

                        uploader.onFileDequeued = function (file) {
                            fileCount--
                            fileSize -= file.size

                            if (!fileCount) {
                                setState('pedding');
                            }

                            removeFile(file)
                            updateTotalProgress()
                        }

                        uploader.on('all', function (type) {
                            var stats

                            switch (type) {
                                case 'uploadFinished':
                                    setState('confirm')

                                    break
                                case 'startUpload':
                                    setState('uploading')

                                    break
                                case 'stopUpload':
                                    setState('paused')

                                    break
                            }
                        })

                        uploader.onError = function (code) {
                            if (code === 'Q_EXCEED_NUM_LIMIT') {
                                BJUI.alertmsg('info', transLang('只允许上传') + options.fileNumLimit + upunit + '！')
                            } else if (code === 'Q_TYPE_DENIED') {
                                BJUI.alertmsg('info', transLang('不支持的文件类型！'))
                            } else if (code === 'F_EXCEED_SIZE') {
                                BJUI.alertmsg('info', transLang('文件太大了！'))
                            } else if (code === 'F_DUPLICATE') {
                                BJUI.alertmsg('info', transLang('已添加过该文件！'))
                            } else {
                                BJUI.alertmsg('info', code)
                            }
                        }

                        $upload.on('click', function () {
                            if ($(this).hasClass('disabled')) {
                                return false
                            }

                            if (state === 'ready') {
                                uploader.upload()
                            } else if (state === 'paused') {
                                uploader.upload()
                            } else if (state === 'uploading') {
                                uploader.stop()
                            }
                        })

                        $info.on('click', '.retry', function () {
                            uploader.retry()
                        })

                        $info.on('click', '.ignore', function () {
                            alert('todo')
                        })

                        $upload.addClass('state-' + state)
                        updateTotalProgress()

                        $element.data('webuploader', uploader).data('webuploader.wrap', $wrap)
                    }
                }
            }

            $box.find('input[data-toggle="webuploader"]').each(function (i) {
                initWebUploader($(this), i)

                $(this).on('reload.webuploader', function () {
                    initWebUploader($(this), i)
                })
            })
        }

        /* fixed dropdown-menu width */
        $box.find('[data-toggle="dropdown"]').parent().on('show.bs.dropdown', function (e) {
            var $this = $(this), width = $this.outerWidth(), $menu = $this.find('> .dropdown-menu'), menuWidth = $menu.outerWidth()

            if (width > menuWidth) {
                $menu.css('min-width', width)
            }
        })

        /* ========================================================================
         * @description highCharts
         * @author 小策一喋 <xvpindex@qq.com>
         * @Blog http://www.topjui.com
         * ======================================================================== */
        var $highcharts = $box.find('[data-toggle="highcharts"]')

        $highcharts.each(function () {
            var $element = $(this)
            var options = $element.data()

            $.get(options.url, function (chartData) {
                $element.highcharts(chartData)
            }, 'json')
        })

        /* ========================================================================
         * @description ECharts
         * @author 小策一喋 <xvpindex@qq.com>
         * @Blog http://www.topjui.com
         * ======================================================================== */
        var $echarts = $box.find('[data-toggle="echarts"]')

        $echarts.each(function () {
            var $element = $(this)
            var options = $element.data()
            var theme = options.theme ? options.theme : 'default'
            var typeArr = options.type.split(',')

            require.config({
                paths: {
                    echarts: BJUI.PLUGINPATH + 'echarts'
                }
            })

            require(
                [
                    'echarts',
                    'echarts/theme/' + theme,
                    'echarts/chart/' + typeArr[0],
                    typeArr[1] ? 'echarts/chart/' + typeArr[1] : 'echarts'
                ],
                function (ec, theme) {
                    var myChart = ec.init($element[0], theme)

                    $.get(options.url, function (chartData) {
                        myChart.setOption(chartData)
                    }, 'json')
                }
            )
        })

    })

}(jQuery);

//datagrid 首行数据查询 下拉框控件的 元素对象
function KVObj(val, key) {
    this.Value = val;
    this.Key = key;
}

//日期格式化
function dateFmt(val, fmt) {
    if (!val) {
        return val;
    }
    var dt = null;
    if (val instanceof Date) {
        dt = new Date(val);
    } else {
        val = val.replace("00:00:00:000", "");
        if (val.length <= 0) {
            return "";
        }
        dt = new Date(val);
    }
    var o = {
        "M+": dt.getMonth() + 1, //月份
        "d+": dt.getDate(), //日
        "H+": dt.getHours(), //小时
        "m+": dt.getMinutes(), //分
        "s+": dt.getSeconds(), //秒
        "q+": Math.floor((dt.getMonth() + 3) / 3), //季度
        "S": dt.getMilliseconds() //毫秒
    };
    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (dt.getFullYear() + "").substr(4 - RegExp.$1.length));
    for (var k in o)
        if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
    return fmt;
}

var lang_en_json =
{
    "错误提示": "False hints", "信息提示": "Information hints", "警告信息": "Warning information", "成功信息": "Success information", "确认信息": "Confirmation of information", "确定": "Sure?", "是": "yes", "否": "no", "取消": "cancel", "关闭": "Close", "最大化": "Maximization", "还原": "reduction", "最小化": "minimize", "弹出窗口": "Pop-up window", "升序": "Ascending order", "降序": "Descending order", "当前页/总页数": "Current page / total page number", "首页": "home page", "末页": "Last", "上一页": "Previous page", "下一页": "next page", "输入跳转页码，回车确认": "Enter jump page number, enter confirm.", "跳转": "Jump", "页": "page", "刷新": "Refresh", "条": "strip", "每页": "each page", "共": "common", "显示/隐藏 列": "Display / hide columns", "过滤": "filter", "清除": "Eliminate", "锁定列": "Lock column", "解除锁定": "Unlock", "添加": "Add to", "编辑": "edit", "保存": "Preservation", "更新": "To update", "删除": "delete", "上一条": "The last one", "下一条": "Next", "查询": "query", "导入": "Import", "导出": "export", "导出筛选": "Export screening", "全部": "whole", "没有数据！": "No data!", "过滤和排序！": "Filtering and sorting!", "点我展开行！": "Point me out!", "点我收缩行！": "Point me to shrink!", "未选中任何行！": "No rows selected!", "请先保存编辑行！": "Please save the edit line first!", "没有需要保存的行！": "There is no row to be saved.", "确定要删除该行吗？": "Are you sure you want to delete the row?", "确定要删除选中行？": "Are you sure you want to delete the selected row?", "未获取到正确的数据！": "Did not get the correct data!", "请求datagrid数据失败！": "Request DataGrid data failed!", "选择选中项": "Select selected items", "追加选择": "Additional selection", "清空现有值": "Empty existing values", "正在努力加载数据，请稍等...": "Trying to load data, please wait...", "上月": "Last month", "下月": "Next month", "清空": "empty", "日": "day", "一": "One", "二": "Two", "三": "Three", "四": "Four", "五": "Five", "六": "Six", "一月": "January", "二月": "February", "三月": "March", "四月": "April", "五月": "May", "六月": "June", "七月": "July", "八月": "August", "九月": "September", "十月": "October", "十一月": "November", "十二月": "December", "刷新本标签": "Refresh the label", "关闭本标签": "Close this label", "关闭其他标签": "Close other labels", "关闭所有标签": "Close all labels", "刷新本窗口": "Refresh this window", "关闭本窗口": "Close this window", "关闭其他窗口": "Close other windows", "关闭所有窗口": "Close all windows", "服务器当前负载过大或者正在维护！": "The server is currently overloaded or is being maintained.", "与服务器的通信中断，请检查URL链接或服务器状态！": "The communication with the server is interrupted. Please check the URL link or server state.", "会话超时，请重新登陆！": "Session timeout, please login again!", "占位符对应的选择器无有效值！": "Placeholder corresponding selector no valid value!", "未定义选中项的组名[复选框的'data-group']！": "The name of the selected item is not defined ['data-group'] of the check box!", "未选中任何一项！": "No item was selected!", "请选择一个选项！": "Please select an option!", "提交的表单中 [{0}] 个字段有错误，请更正后再提交！": "There are errors in the [{0}] fields of the submitted forms. Please correct them before submitting them.", "不规范，ID需以字母开头，组成部分包括（0-9，字母，中横线，下划线）": "Nonstandard, ID needs to start with letters, including components (0-9, alphabet, middle line, underline).", "我的主页": "My home page", "请输入整数": "please enter an integer", "请输入有效的数字": "Please enter a valid number.", "{0}只能输入字母": "{0} can only input letters.", "只能输入大写字母和数字": "You can only input uppercase letters and numbers.", "手机号格式不正确": "The phone number is not correct.", "QQ号格式不正确": "The format of QQ is incorrect.", "请输入正确的日期，例：yyyy-MM-dd": "Please enter the correct date. Example: yyyy-MM-dd", "请输入正确的时间，例：HH:mm:ss": "Please input the correct time. Example: HH:mm:ss", "请输入正确的日期时间，例：yyyy-MM-dd HH:mm:ss": "Please enter the correct date time, for example: yyyy-MM-dd HH:mm:ss", "请输入中文": "Please input Chinese.", "请输入3-12位数字、字母、下划线": "Please input 3-12 digits, letters, underline.", "错误的日期时间格式！": "Wrong date time format!", "更多查询条件": "More query criteria", "新增": "Newly added", "确认要删除 ": "Confirm to delete", "张图片": "Picture", "查看已上传图片": "View uploaded pictures", "向右旋转": "Turn right", "向左旋转": "Revolving to the left", "文件大小超出": "File size exceeded", "上传暂停": "Upload pause", "上传失败，请重试": "Upload failed, please try again.", "预览中": "Preview", "不能预览": "Not Preview", "选中": "Select", "已成功上传": "Uploaded successfully", "上传失败": "Upload failure", "重新上传": "Re - Upload", "或": "or", "忽略": "ignore", "已上传": "Uploaded", "失败": "fail", "暂停上传": "Pause upload", "继续上传": "Continue uploading", "开始上传": "Start uploading", "不支持您的浏览器！如果你使用的是IE浏览器，请尝试升级 flash 播放器": "Do not support your browser! If you are using IE browser, please try upgrading flash player.", "点击选择图片": "Click to select pictures.", "继续添加": "Continue to add", "只允许上传": "Upload only", "不支持的文件类型！": "Unsupported file types!", "文件太大了！": "The file is too big!", "已添加过该文件！": "The file has been added!", "汉语菜单项": "Chinese menu item", "英语菜单项": "English menu items", "日语菜单项": "Japanese menu item", "韩语菜单项": "Korean menu item", "西班牙语菜单项": "Spanish menu item", "ID": "ID", "图标": "Icon", "url": "URL", "父ID": "Father ID", "是否禁用": "Whether or not to disable", "启用": "Enable", "禁用": "Disable", "排序": "sort", "操作": "operation", "清空查询": "Empty query", "添加时间": "Add time", "用户": "user", "登录名": "Login name", "密码": "Password", "真实名": "Real name", "昵称": "Nickname?", "性别": "Sex", "证件类型": "Document type", "证件号码": "Identification Number", "使用语言": "Use language", "用户类型": "customer type", "公司简称": "Company abbreviation", "联系电话": "Contact number", "邮件地址": "mailing address", "运营商串": "Operator string", "状态": "state", "最后修改时间": "Last modification time", "超级用户": "Superuser", "管理用户": "Managing users", "代理用户": "Proxy user", "个人用户": "Personal user", "实时翻译": "Real time translation", "设置语言出错！": "Setup language error!", "实时翻译时出错！": "Error in real-time translation.", "已经翻译的是否需要替换？": "Does translation need to be replaced?", "菜单名称已翻译完成！": "The name of the menu has been translated.", "修改成功！": "Modified successfully!", "删除成功！": "Deleted successfully!", "特大": "XL", "大": "L", "中": "M", "小": "S"
};
var lang_jp_json =
{
    "错误提示": "エラーのヒント", "信息提示": "情報のヒント", "警告信息": "警告情報", "成功信息": "成功情報", "确认信息": "情報を確認する", "确定": "確定する", "是": "は", "否": "いいえ", "取消": "取り消す", "关闭": "閉鎖する", "最大化": "最大化", "还原": "還元する", "最小化": "最小化", "弹出窗口": "ポップアップウィンドウ", "升序": "昇順", "降序": "順序を下げる", "当前页/总页数": "現在のページ/総ページ数", "首页": "トップページ", "末页": "末のページ", "上一页": "上一頁", "下一页": "次のページ", "输入跳转页码，回车确认": "ジャンプページ番号を入力して、リカー確認", "跳转": "飛び回る", "页": "ページ", "刷新": "更新する", "条": "条", "每页": "ページごと", "共": "共にする", "显示/隐藏 列": "表示/隠し列", "过滤": "濾過する", "清除": "除去する", "锁定列": "列をロックする", "解除锁定": "ロック解除", "添加": "追加", "编辑": "編集する", "保存": "保存する", "更新": "更新する", "删除": "削除", "上一条": "1本上", "下一条": "次の条", "查询": "クエリ", "导入": "導入する", "导出": "導き出す", "导出筛选": "スクリーニングを導き出す", "全部": "すべて", "没有数据！": "データがない！", "过滤和排序！": "フィルタとソート！", "点我展开行！": "私が展開していきます！", "点我收缩行！": "私は収縮します！", "未选中任何行！": "何も選ばない！", "请先保存编辑行！": "先に編集者を保存してください！", "没有需要保存的行！": "保存する必要はありません！", "确定要删除该行吗？": "この行を削除しますか？", "确定要删除选中行？": "選択した行を削除しますか？", "未获取到正确的数据！": "正しいデータは入手できません！", "请求datagrid数据失败！": "datagirdデータの失敗をお願いします！", "选择选中项": "選択項目を選択する", "追加选择": "追加選択", "清空现有值": "清空の既存値", "正在努力加载数据，请稍等...": "データのロードを頑張っていますので、少々お待ちください。", "上月": "先月", "下月": "来月", "清空": "空を清める", "日": "日", "一": "1", "二": "2", "三": "3", "四": "4", "五": "5", "六": "6", "一月": "1月", "二月": "2月", "三月": "3月", "四月": "四月", "五月": "5月", "六月": "6月", "七月": "7月", "八月": "8月", "九月": "9月", "十月": "10月", "十一月": "11月", "十二月": "12月", "刷新本标签": "本ラベルを更新する", "关闭本标签": "ラベルを閉じる", "关闭其他标签": "他のラベルを閉じる", "关闭所有标签": "すべてのラベルを閉じる", "刷新本窗口": "この窓口を更新する", "关闭本窗口": "このウィンドウを閉じる", "关闭其他窗口": "他のウィンドウを閉じる", "关闭所有窗口": "すべてのウィンドウを閉じる", "服务器当前负载过大或者正在维护！": "サーバーの負荷が大きくなり、メンテナンス中です！", "与服务器的通信中断，请检查URL链接或服务器状态！": "サーバーとの通信が中断され、URLリンクやサーバ状態をチェックしてください！", "会话超时，请重新登陆！": "時間を超えて、再登録してください！", "占位符对应的选择器无有效值！": "1位に対応するセレクタに有効値がありません！", "未定义选中项的组名[复选框的'data-group']！": "選択項目のグループ名が定義されていません。", "未选中任何一项！": "何も選ばない！", "请选择一个选项！": "オプションを選択してください！", "提交的表单中 [{0}] 个字段有错误，请更正后再提交！": "提出した表の中で「{ 0 }」のフィールドにエラーがありますので、訂正してから提出してください。", "不规范，ID需以字母开头，组成部分包括（0-9，字母，中横线，下划线）": "IDはアルファベットで始め、構成部分（0 - 9、アルファベット、中横線、下線）。", "我的主页": "私のホームページ", "请输入整数": "整数を入力してください", "请输入有效的数字": "有効な数字を入力してください。", "{0}只能输入字母": "{ 0 }はアルファベットのみ", "只能输入大写字母和数字": "文字と数字を入力することしかできません", "手机号格式不正确": "携帯番号のフォーマットが正しくない", "QQ号格式不正确": "QQ番号のフォーマットは正しくありません", "请输入正确的日期，例：yyyy-MM-dd": "正しい日付を入力してください。", "请输入正确的时间，例：HH:mm:ss": "正しい時間を入力してください。", "请输入正确的日期时间，例：yyyy-MM-dd HH:mm:ss": "正しい日付の時間を入力してください：yyyy - mm - dd HH : mm : ss", "请输入中文": "中国語を入力してください", "请输入3-12位数字、字母、下划线": "3 - 12位の数字、アルファベット、下線を入力してください", "错误的日期时间格式！": "エラーの日付のフォーマット！", "更多查询条件": "検索条件", "新增": "新たに", "确认要删除 ": "削除を確認する", "张图片": "写真の写真", "查看已上传图片": "アップロード画像をチェック", "向右旋转": "右に回転する", "向左旋转": "左に回る", "文件大小超出": "ファイルサイズ", "上传暂停": "アップロード停止", "上传失败，请重试": "アップロードに失敗したので、やり直してください", "预览中": "プレビュー", "不能预览": "プレビューできません", "选中": "選択する", "已成功上传": "成功したアップロード", "上传失败": "アップロード失敗", "重新上传": "アップロード", "或": "または", "忽略": "無視する", "已上传": "アップロード", "失败": "失敗する", "暂停上传": "アップロードを停止", "继续上传": "アップロード", "开始上传": "アップロード開始", "不支持您的浏览器！如果你使用的是IE浏览器，请尝试升级 flash 播放器": "ブラウザをサポートしていません！IEブラウザを使っている場合は、flashプレーヤーをアップグレードしてみてください", "点击选择图片": "クリックした画像をクリック", "继续添加": "追加追加", "只允许上传": "アップロードするだけ", "不支持的文件类型！": "サポートしないファイルタイプ！", "文件太大了！": "ファイルが大きすぎる！", "已添加过该文件！": "ファイルを追加しました！", "汉语菜单项": "中国語のメニュー", "英语菜单项": "英語のメニュー", "日语菜单项": "日本語のメニュー", "韩语菜单项": "韓国語のメニュー", "西班牙语菜单项": "スペイン語のメニュー", "ID": "ID", "图标": "アイコン", "url": "url", "父ID": "父のID", "是否禁用": "使用禁止", "启用": "有効にする", "禁用": "禁用", "排序": "並べ替え", "操作": "操作する", "清空查询": "検索", "添加时间": "時間を追加する", "用户": "ユーザー", "登录名": "登録名", "密码": "パスワード", "真实名": "真実の名", "昵称": "ニックネーム", "性别": "性別", "证件类型": "証明書のタイプ", "证件号码": "証明書の番号", "使用语言": "言葉を使う", "用户类型": "ユーザータイプ", "公司简称": "会社の略称", "联系电话": "電話に連絡する", "邮件地址": "メールアドレス", "运营商串": "運営商", "状态": "状態", "最后修改时间": "最後の修正時間", "超级用户": "スーパーユーザー", "管理用户": "ユーザーを管理する", "代理用户": "代理人", "个人用户": "個人ユーザー", "实时翻译": "リアルタイムの翻訳", "设置语言出错！": "言語のエラーを設定します！", "实时翻译时出错！": "リアルタイムの翻訳で間違い！", "已经翻译的是否需要替换？": "翻訳したのは替えが必要ですか？", "菜单名称已翻译完成！": "メニュー名は翻訳完了！", "修改成功！": "修正成功！", "删除成功！": "削除成功！", "特大": "特大", "大": "大きい", "中": "中に", "小": "小さい"
};
var lang_kor_json =
{
    "错误提示": "오류 힌트", "信息提示": "정보 힌트", "警告信息": "경고 메시지", "成功信息": "정보", "确认信息": "확인 정보", "确定": "확정하다", "是": "네.", "否": "여부", "取消": "취소", "关闭": "폐쇄하다.", "最大化": "최대화", "还原": "환원", "最小化": "최소화", "弹出窗口": "창 꺼내기", "升序": "승진", "降序": "강서", "当前页/总页数": "현재 페이지 / 총 페이지", "首页": "페이지", "末页": "끝", "上一页": "페이지", "下一页": "다음 페이지", "输入跳转页码，回车确认": "다이빙 페이지먼트 입력, 회차 확인", "跳转": "다이빙", "页": "페이지", "刷新": "새로 고침", "条": "조목", "每页": "매 페이지", "共": "함께", "显示/隐藏 列": "디스플레이 / 숨기기", "过滤": "여과하다.", "清除": "없애다", "锁定列": "선정", "解除锁定": "자물쇠 를 해제 하다", "添加": "가첨", "编辑": "편집하다.", "保存": "보존", "更新": "갱신", "删除": "삭제", "上一条": "이전", "下一条": "다음", "查询": "조회하다.", "导入": "가져오기", "导出": "내보내기", "导出筛选": "선별 안내", "全部": "모두.", "没有数据！": "데이터가 없습니다!", "过滤和排序！": "필터와 순서!", "点我展开行！": "내가 펼쳐!", "点我收缩行！": "제가 수축해 주세요!", "未选中任何行！": "모든 줄 선택하지 않았습니다!", "请先保存编辑行！": "우선 편집실을 저장해 주십시오!", "没有需要保存的行！": "저장할 수 있는 줄 없습니다!", "确定要删除该行吗？": "이 줄을 삭제하시겠습니까?", "确定要删除选中行？": "선택된 줄을 삭제하시겠습니까?", "未获取到正确的数据！": "올바른 데이터를 가져오지 않았습니다!", "请求datagrid数据失败！": "data rid 데이터를 요청하기 실패!", "选择选中项": "선택된 항목 선택", "追加选择": "추가 선택", "清空现有值": "청공값", "正在努力加载数据，请稍等...": "데이터를 열심히 중재하고 있으니 잠시 기다려 주십시오...", "上月": "지난 달", "下月": "내달", "清空": "허공", "日": "날", "一": "한", "二": "두", "三": "삼", "四": "사", "五": "오", "六": "여섯", "一月": "일월", "二月": "이월", "三月": "삼월.", "四月": "사월", "五月": "월, 오월", "六月": "유월 유월", "七月": "칠월 칠월", "八月": "팔월", "九月": "구월", "十月": "시월", "十一月": "십일월", "十二月": "십이월", "刷新本标签": "본 탭 새로 고침", "关闭本标签": "본 태그 닫기", "关闭其他标签": "다른 탭 닫기", "关闭所有标签": "모든 탭 닫기", "刷新本窗口": "창 새로 고침", "关闭本窗口": "이 창 닫기", "关闭其他窗口": "다른 창 닫기", "关闭所有窗口": "창 닫기", "服务器当前负载过大或者正在维护！": "서버가 현재 마이너스 크거나 유지하고 있습니다!", "与服务器的通信中断，请检查URL链接或服务器状态！": "서버의 통신이 중단되었습니다. URL 링크나 서버 상태를 확인하십시오!", "会话超时，请重新登陆！": "세션 시간이 초과되면 다시 상륙해 주세요.", "占位符对应的选择器无有效值！": "비트릭스 선택의 값어치 없음!", "未定义选中项的组名[复选框的'data-group']！": "선택된 항목 이름을 정의하지 않았습니다. [복선 상자 'data - Gatoup']!", "未选中任何一项！": "한 항목이 선택되지 않았습니다!", "请选择一个选项！": "선택 옵션을 선택하세요!", "提交的表单中 [{0}] 个字段有错误，请更正后再提交！": "제출한 표 중 [en 0 _] 중 [en 0]] 글자에 오류가 있으니, 다시 정정하신 후에 다시 제출하세요.", "不规范，ID需以字母开头，组成部分包括（0-9，字母，中横线，下划线）": "규범, 알파벳 시작 부분, 구성 부분 포함 (0 - 9, 알파벳, 중횡선, 밑줄)", "我的主页": "내 홈페이지", "请输入整数": "정수를 입력하세요", "请输入有效的数字": "유효한 숫자를 입력하십시오.", "{0}只能输入字母": "r 0. 알파벳만 입력할 수 있다", "只能输入大写字母和数字": "대문자와 숫자만 입력할 수 있을 수밖에 없다", "手机号格式不正确": "휴대폰호 형식이 정확하지 않다.", "QQ号格式不正确": "QQ 호 형식이 부정확하다", "请输入正确的日期，例：yyyy-MM-dd": "올바른 날짜 입력하십시오. 예: yyyyy - MM - dd", "请输入正确的时间，例：HH:mm:ss": "올바른 시간 입력: HH: mm: S", "请输入正确的日期时间，例：yyyy-MM-dd HH:mm:ss": "올바른 날짜 입력하십시오: yyyymM - MM - dd HH: mm:%", "请输入中文": "중국어 입력", "请输入3-12位数字、字母、下划线": "3 - 12 분의 숫자, 알파벳, 하선을 입력하십시오.", "错误的日期时间格式！": "잘못된 날짜 시간 형식!", "更多查询条件": "추가 조회 조건", "新增": "새로 늘다", "确认要删除 ": "삭제할 수 있습니다.", "张图片": "사진", "查看已上传图片": "업로드된 그림 보기", "向右旋转": "오른쪽으로 회전", "向左旋转": "왼쪽으로 돌다.", "文件大小超出": "파일 크기 초과", "上传暂停": "업로드 타임", "上传失败，请重试": "업로드 실패, 다시 시험해 보십시오.", "预览中": "미리 보기", "不能预览": "예람할 수 없습니다.", "选中": "선택하다", "已成功上传": "성공적으로 올립니다.", "上传失败": "업로드 실패", "重新上传": "다시 올린다", "或": "혹", "忽略": "무시", "已上传": "업로드", "失败": "실패하다", "暂停上传": "일시 정지", "继续上传": "계속 업로드", "开始上传": "시작.", "不支持您的浏览器！如果你使用的是IE浏览器，请尝试升级 flash 播放器": "웹 브라우저를 지원하지 않습니다!IE 브라우저를 사용하면 flash 플레이어를 업그레이드해 보십시오.", "点击选择图片": "선택 그림 클릭", "继续添加": "계속 추가", "只允许上传": "업로드 허용", "不支持的文件类型！": "지원하지 않는 파일 형식!", "文件太大了！": "문서가 너무 커요!", "已添加过该文件！": "이 파일이 추가되었습니다!", "汉语菜单项": "중국어 메뉴 항목.", "英语菜单项": "영어 메뉴 항목", "日语菜单项": "일본어 메뉴", "韩语菜单项": "한국어 메뉴", "西班牙语菜单项": "스페인어 메뉴", "ID": "ID", "图标": "아이콘", "url": "url", "父ID": "아버지", "是否禁用": "사용하지 않는가", "启用": "활성화", "禁用": "사용 금지", "排序": "정렬", "操作": "조작하다.", "清空查询": "자세히 조회하다.", "添加时间": "추가 시간", "用户": "사용자", "登录名": "로그인", "密码": "암호", "真实名": "진실한 이름", "昵称": "별명, 별명", "性别": "성별.", "证件类型": "증거물 유형", "证件号码": "번호", "使用语言": "사용 언어", "用户类型": "사용자 종류", "公司简称": "회사 약칭", "联系电话": "연락", "邮件地址": "메일 주소", "运营商串": "상담을 운영하다", "状态": "상태, 상태", "最后修改时间": "마지막 수정 시간", "超级用户": "슈퍼 사용자", "管理用户": "관리 사용자", "代理用户": "에이전트 사용자", "个人用户": "개인 사용자", "实时翻译": "실시간 번역", "设置语言出错！": "언어 설정 오류!", "实时翻译时出错！": "실시간 번역 중 오류!", "已经翻译的是否需要替换？": "변경할 수 있는 것은 대체할 필요가 있습니까?", "菜单名称已翻译完成！": "메뉴 이름 번역이 완료되었습니다!", "修改成功！": "수정 성공!", "删除成功！": "삭제 성공!", "特大": "특대", "大": "크다", "中": "중", "小": "작다"
};
var lang_spa_json =
{
    "错误提示": "Error", "信息提示": "Información rápida", "警告信息": "El mensaje de advertencia", "成功信息": "Mensaje de éxito", "确认信息": "Confirmación de la información", "确定": "Determinar", "是": "Sí.", "否": "No", "取消": "Cancelar", "关闭": "Cierre", "最大化": "Maximizar", "还原": "Devuelta", "最小化": "Minimizar", "弹出窗口": "Ventana de salida", "升序": "Recargar", "降序": "Desorden", "当前页/总页数": "Página actual / total de páginas", "首页": "La página inicial", "末页": "Página", "上一页": "En la página anterior", "下一页": "En la siguiente página", "输入跳转页码，回车确认": "Salto de página de entrada, pulse Intro", "跳转": "Saltar", "页": "Página", "刷新": "Refrescar", "条": "Artículo", "每页": "Cada página", "共": "Total", "显示/隐藏 列": "Mostrar / ocultar", "过滤": "La filtración", "清除": "La eliminación de", "锁定列": "Encerrado", "解除锁定": "Levantar el bloqueo", "添加": "Añadir", "编辑": "Editar", "保存": "Guardar", "更新": "La actualización", "删除": "Borrar", "上一条": "En un artículo", "下一条": "El siguiente artículo", "查询": "La consulta", "导入": "Entrada", "导出": "Exportar", "导出筛选": "Selección de selección", "全部": "Todo", "没有数据！": "¡No hay datos!", "过滤和排序！": "Ordenar y filtrar.", "点我展开行！": "¡Mi lanza!", "点我收缩行！": "- Sí.", "未选中任何行！": "¡Se desmarcan de cualquier fila!", "请先保存编辑行！": "Por favor, mantenga la línea de edición.", "没有需要保存的行！": "¡No hay necesidad de guardar la línea!", "确定要删除该行吗？": "¿Seguro que quieres borrar la línea?", "确定要删除选中行？": "¿Está seguro de que para borrar la línea seleccionada?", "未获取到正确的数据！": "¡No se dispone de los datos correctos!", "请求datagrid数据失败！": "¡Solicitud de datos datagrid fracaso!", "选择选中项": "La selección de elementos seleccionados", "追加选择": "Opciones adicionales", "清空现有值": "Vaciar el valor actual", "正在努力加载数据，请稍等...": "Tratando de cargar los datos, espera un minuto.", "上月": "El mes pasado.", "下月": "El mes que viene.", "清空": "Vaciar", "日": "Día", "一": "I", "二": "Dos.", "三": "Tres.", "四": "Cuatro.", "五": "Cinco.", "六": "6", "一月": "De enero de", "二月": "De febrero de", "三月": "Marzo", "四月": "Abril", "五月": "En mayo", "六月": "Junio", "七月": "De julio de", "八月": "De agosto de", "九月": "Septiembre", "十月": "De octubre de", "十一月": "De noviembre de", "十二月": "De diciembre de", "刷新本标签": "Refrescar la etiqueta", "关闭本标签": "Cerrar la etiqueta", "关闭其他标签": "El cierre de otras etiquetas", "关闭所有标签": "Cierren todas las etiquetas.", "刷新本窗口": "Refrescar la ventana", "关闭本窗口": "Cierre de esta ventana", "关闭其他窗口": "Cerrar otras ventanas", "关闭所有窗口": "Cierren todas las ventanas.", "服务器当前负载过大或者正在维护！": "El servidor de carga es demasiado grande o en mantenimiento.", "与服务器的通信中断，请检查URL链接或服务器状态！": "¡Interrumpida la comunicación con el servidor, verifique el Estado del enlace o el Estado del servidor!", "会话超时，请重新登陆！": "¡Sesión de la sesion de nuevo, por favor!", "占位符对应的选择器无有效值！": "¡El selector no tiene valor!", "未定义选中项的组名[复选框的'data-group']！": "No se define el nombre del grupo seleccionado", "未选中任何一项！": "¡No marque ninguno!", "请选择一个选项！": "¡Seleccione una opción!", "提交的表单中 [{0}] 个字段有错误，请更正后再提交！": "Hay un error en las palabras que se presentó, por favor, corregirlo.", "不规范，ID需以字母开头，组成部分包括（0-9，字母，中横线，下划线）": "Id no estándar, debe comenzar con una letra, incluye (0 - 9, en la letra, línea, subrayado)", "我的主页": "Mi página principal", "请输入整数": "Introduzca los números completos, por favor.", "请输入有效的数字": "Por favor, introduzca cifras efectivas, por favor.", "{0}只能输入字母": "{0} sólo el alfabeto de entrada", "只能输入大写字母和数字": "Sólo se introducen letras mayúsculas y números.", "手机号格式不正确": "No es correcto formato móvil.", "QQ号格式不正确": "El formulario QQ es incorrecto.", "请输入正确的日期，例：yyyy-MM-dd": "Ejemplo escriba la fecha: yyyy-MM-dd", "请输入正确的时间，例：HH:mm:ss": "Por favor, introduzca el tiempo correcto, por favor.", "请输入正确的日期时间，例：yyyy-MM-dd HH:mm:ss": "Por favor introduzca la fecha correcta, por ejemplo: YYY - MM - DL HH: SS: SS: SS: SS: SS: SS:", "请输入中文": "Por favor, introduzca la palabra en chino.", "请输入3-12位数字、字母、下划线": "Por favor, introduzca 3 - 12 números, letras, subdivisiones.", "错误的日期时间格式！": "¡Formato incorrecto de fecha!", "更多查询条件": "Más condiciones de consulta", "新增": "Añadir", "确认要删除 ": "Estás seguro de que quieres borrar", "张图片": "Foto.", "查看已上传图片": "Ver subir fotos", "向右旋转": "Girar a la derecha", "向左旋转": "Gira a la izquierda.", "文件大小超出": "Más allá del tamaño de archivo", "上传暂停": "Carga suspendida", "上传失败，请重试": "Inténtalo, por favor.", "预览中": "Previsualización", "不能预览": "No se puede predecir", "选中": "Seleccionado", "已成功上传": "Se ha enviado con éxito", "上传失败": "Cargar el fracaso", "重新上传": "Volver a subir", "或": "O", "忽略": "Ignorar", "已上传": "Ya está.", "失败": "Fracasar", "暂停上传": "La suspensión de carga", "继续上传": "Subir", "开始上传": "Empiecen a subir.", "不支持您的浏览器！如果你使用的是IE浏览器，请尝试升级 flash 播放器": "¡No estoy de acuerdo con su navegador!Si usaste un navegador IE, por favor, intenten actualizar el reproductor de flash.", "点击选择图片": "Pulse en la selección", "继续添加": "Añadir", "只允许上传": "Sólo permite subir", "不支持的文件类型！": "¡Tipo de archivo no apoyado!", "文件太大了！": "¡El archivo es demasiado grande!", "已添加过该文件！": "¡Se ha añadido el documento!", "汉语菜单项": "El menú chino", "英语菜单项": "Menú de inglés", "日语菜单项": "Menú de idioma japonés", "韩语菜单项": "El menú de coreano.", "西班牙语菜单项": "Menú de español", "ID": "ID", "图标": "Icono", "url": "URL", "父ID": "El padre de Id", "是否禁用": "Si desactiva", "启用": "Activar", "禁用": "Desactivar", "排序": "Subordinación", "操作": "La operación", "清空查询": "Vaciar", "添加时间": "Añadir tiempo", "用户": "Usuario", "登录名": "El nombre de inicio de sesión", "密码": "Contraseña", "真实名": "Nombre real", "昵称": "Apodo", "性别": "El género", "证件类型": "Tipo de documento", "证件号码": "Número de identificación", "使用语言": "Uso de la lengua", "用户类型": "Tipo de usuario", "公司简称": "La empresa denominada", "联系电话": "Teléfono de contacto", "邮件地址": "Dirección de correo", "运营商串": "Explotador", "状态": "Estado", "最后修改时间": "Tiempo de revisión", "超级用户": "Superusuario", "管理用户": "Usuarios de gestión", "代理用户": "El agente de usuario", "个人用户": "Usuario personal", "实时翻译": "Traductor en tiempo real", "设置语言出错！": "¡Configurar el idioma de error!", "实时翻译时出错！": "Un error de traducción en tiempo real.", "已经翻译的是否需要替换？": "¿Debe sustituirse la traducción ya traducida?", "菜单名称已翻译完成！": "¡El nombre del menú se ha traducido ya!", "修改成功！": "¡Modificar el éxito!", "删除成功！": "¡Eliminado con éxito!", "特大": "Gran", "大": "Grande", "中": "Media", "小": "Pequeño."
};



function transLang(strText) {
    var lang = $.cookie("LANGUAGE") || "zh";
    if (lang === "zh") {
        return strText;
    } else if (lang === "en") {
        return lang_en_json[strText] || strText;
    }
    else if (lang === "jp") {
        return lang_jp_json[strText] || strText;
    } else if (lang === "kor") {
        return lang_kor_json[strText] || strText;
    } else if (lang === "spa") {
        return lang_spa_json[strText] || strText;
    } else {
        return strText;
    }
}
